+#endif
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CButtonST
+
+// Mask for control's type
+#ifdef BS_TYPEMASK
+#undef BS_TYPEMASK
+#endif
+#define BS_TYPEMASK SS_TYPEMASK
+
+CButtonST::CButtonST()
+{
+ m_bIsPressed = FALSE;
+ m_bIsFocused = FALSE;
+ m_bIsDisabled = FALSE;
+ m_bMouseOnButton = FALSE;
+ leftAligned = false;
+ marked = false;
+
+ FreeResources(FALSE);
+
+ // Default type is "flat" button
+ m_bIsFlat = TRUE;
+ // Button will be tracked also if when the window is inactive (like Internet Explorer)
+ m_bAlwaysTrack = TRUE;
+
+ // By default draw border in "flat" button
+ m_bDrawBorder = TRUE;
+
+ // By default icon is aligned horizontally
+ m_byAlign = ST_ALIGN_HORIZ;
+
+ // By default use usual pressed style
+ SetPressedStyle(BTNST_PRESSED_LEFTRIGHT, FALSE);
+
+ // By default, for "flat" button, don't draw the focus rect
+ m_bDrawFlatFocus = FALSE;
+
+ // By default the button is not the default button
+ m_bIsDefault = FALSE;
+ // Invalid value, since type still unknown
+ m_nTypeStyle = BS_TYPEMASK;
+
+ // By default the button is not a checkbox
+ m_bIsCheckBox = FALSE;
+ m_nCheck = 0;
+
+ // Set default colors
+ SetDefaultColors(FALSE);
+
+ // No tooltip created
+ m_ToolTip.m_hWnd = NULL;
+
+ // Do not draw as a transparent button
+ m_bDrawTransparent = FALSE;
+ m_pbmpOldBk = NULL;
+
+ // No URL defined
+ SetURL(NULL);
+
+ // No cursor defined
+ m_hCursor = NULL;
+
+ // No associated menu
+#ifndef BTNST_USE_BCMENU
+ m_hMenu = NULL;
+#endif
+ m_hParentWndMenu = NULL;
+ m_bMenuDisplayed = FALSE;
+
+ m_bShowDisabledBitmap = TRUE;
+
+ m_ptImageOrg.x = 3;
+ m_ptImageOrg.y = 3;
+
+ // No defined callbacks
+ ::ZeroMemory(&m_csCallbacks, sizeof(m_csCallbacks));
+
+#ifdef BTNST_USE_SOUND
+ // No defined sounds
+ ::ZeroMemory(&m_csSounds, sizeof(m_csSounds));
+#endif
+} // End of CButtonST
+
+CButtonST::~CButtonST()
+{
+ // Restore old bitmap (if any)
+ if (m_dcBk.m_hDC && m_pbmpOldBk)
+ {
+ m_dcBk.SelectObject(m_pbmpOldBk);
+ } // if
+
+ FreeResources();
+
+ // Destroy the cursor (if any)
+ if (m_hCursor) ::DestroyCursor(m_hCursor);
+
+ // Destroy the menu (if any)
+#ifdef BTNST_USE_BCMENU
+ if (m_menuPopup.m_hMenu) VERIFY( m_menuPopup.DestroyMenu() );
+#else
+ if (m_hMenu) VERIFY( ::DestroyMenu(m_hMenu) );
+#endif
+} // End of ~CButtonST
+
+BEGIN_MESSAGE_MAP(CButtonST, CButton)
+ //{{AFX_MSG_MAP(CButtonST)
+ ON_WM_SETCURSOR()
+ ON_WM_KILLFOCUS()
+ ON_WM_MOUSEMOVE()
+ ON_WM_SYSCOLORCHANGE()
+ ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
+ ON_WM_ACTIVATE()
+ ON_WM_ENABLE()
+ ON_WM_CANCELMODE()
+ ON_WM_GETDLGCODE()
+ ON_WM_CTLCOLOR_REFLECT()
+ //}}AFX_MSG_MAP
+#ifdef BTNST_USE_BCMENU
+ ON_WM_MENUCHAR()
+ ON_WM_MEASUREITEM()
+#endif
+
+ ON_MESSAGE(BM_SETSTYLE, OnSetStyle)
+ ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
+ ON_MESSAGE(BM_SETCHECK, OnSetCheck)
+ ON_MESSAGE(BM_GETCHECK, OnGetCheck)
+END_MESSAGE_MAP()
+
+void CButtonST::FreeResources(BOOL bCheckForNULL)
+{
+ if (bCheckForNULL)
+ {
+ // Destroy icons
+ // Note: the following two lines MUST be here! even if
+ // BoundChecker says they are unnecessary!
+ if (m_csIcons[0].hIcon)
+ VERIFY( ::DestroyIcon(m_csIcons[0].hIcon) );
+ if (m_csIcons[1].hIcon)
+ VERIFY( ::DestroyIcon(m_csIcons[1].hIcon) );
+
+ // Destroy bitmaps
+ if (m_csBitmaps[0].hBitmap)
+ VERIFY( ::DeleteObject(m_csBitmaps[0].hBitmap) );
+ if (m_csBitmaps[1].hBitmap)
+ VERIFY( ::DeleteObject(m_csBitmaps[1].hBitmap) );
+
+ // Destroy mask bitmaps
+ if (m_csBitmaps[0].hMask)
+ VERIFY( ::DeleteObject(m_csBitmaps[0].hMask) );
+ if (m_csBitmaps[1].hMask)
+ VERIFY( ::DeleteObject(m_csBitmaps[1].hMask) );
+ } // if
+
+ ::ZeroMemory(&m_csIcons, sizeof(m_csIcons));
+ ::ZeroMemory(&m_csBitmaps, sizeof(m_csBitmaps));
+} // End of FreeResources
+
+void CButtonST::PreSubclassWindow()
+{
+ UINT nBS;
+
+ nBS = GetButtonStyle();
+
+ // Set initial control type
+ m_nTypeStyle = nBS & BS_TYPEMASK;
+
+ // Check if this is a checkbox
+ if (nBS & BS_CHECKBOX) m_bIsCheckBox = TRUE;
+
+ // Set initial default state flag
+ if (m_nTypeStyle == BS_DEFPUSHBUTTON)
+ {
+ // Set default state for a default button
+ m_bIsDefault = TRUE;
+
+ // Adjust style for default button
+ m_nTypeStyle = BS_PUSHBUTTON;
+ } // If
+
+ // You should not set the Owner Draw before this call
+ // (don't use the resource editor "Owner Draw" or
+ // ModifyStyle(0, BS_OWNERDRAW) before calling PreSubclassWindow() )
+ ASSERT(m_nTypeStyle != BS_OWNERDRAW);
+
+ // Switch to owner-draw
+ ModifyStyle(BS_TYPEMASK, BS_OWNERDRAW, SWP_FRAMECHANGED);
+
+ CButton::PreSubclassWindow();
+} // End of PreSubclassWindow
+
+UINT CButtonST::OnGetDlgCode()
+{
+ UINT nCode = CButton::OnGetDlgCode();
+
+ // Tell the system if we want default state handling
+ // (losing default state always allowed)
+ nCode |= (m_bIsDefault ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON);
+
+ return nCode;
+} // End of OnGetDlgCode
+
+BOOL CButtonST::PreTranslateMessage(MSG* pMsg)
+{
+ InitToolTip();
+ m_ToolTip.RelayEvent(pMsg);
+
+ if (pMsg->message == WM_LBUTTONDBLCLK)
+ pMsg->message = WM_LBUTTONDOWN;
+
+ return CButton::PreTranslateMessage(pMsg);
+} // End of PreTranslateMessage
+
+HBRUSH CButtonST::CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/)
+{
+ return (HBRUSH)::GetStockObject(NULL_BRUSH);
+} // End of CtlColor
+
+void CButtonST::OnSysColorChange()
+{
+ CButton::OnSysColorChange();
+
+ m_dcBk.DeleteDC();
+ m_bmpBk.DeleteObject();
+ SetDefaultColors();
+} // End of OnSysColorChange
+
+LRESULT CButtonST::OnSetStyle(WPARAM wParam, LPARAM lParam)
+{
+ UINT nNewType = (wParam & BS_TYPEMASK);
+
+ // Update default state flag
+ if (nNewType == BS_DEFPUSHBUTTON)
+ {
+ m_bIsDefault = TRUE;
+ } // if
+ else if (nNewType == BS_PUSHBUTTON)
+ {
+ // Losing default state always allowed
+ m_bIsDefault = FALSE;
+ } // if
+
+ // Can't change control type after owner-draw is set.
+ // Let the system process changes to other style bits
+ // and redrawing, while keeping owner-draw style
+ return DefWindowProc(BM_SETSTYLE,
+ (wParam & ~BS_TYPEMASK) | BS_OWNERDRAW, lParam);
+} // End of OnSetStyle
+
+LRESULT CButtonST::OnSetCheck(WPARAM wParam, LPARAM /*lParam*/)
+{
+ ASSERT(m_bIsCheckBox);
+
+ switch (wParam)
+ {
+ case BST_CHECKED:
+ case BST_INDETERMINATE: // Indeterminate state is handled like checked state
+ SetCheck(1);
+ break;
+ default:
+ SetCheck(0);
+ break;
+ } // switch
+
+ return 0;
+} // End of OnSetCheck
+
+LRESULT CButtonST::OnGetCheck(WPARAM /*wParam*/, LPARAM /*lParam*/)
+{
+ ASSERT(m_bIsCheckBox);
+ return GetCheck();
+} // End of OnGetCheck
+
+#ifdef BTNST_USE_BCMENU
+LRESULT CButtonST::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu)
+{
+ LRESULT lResult;
+ if (BCMenu::IsMenu(pMenu))
+ lResult = BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
+ else
+ lResult = CButton::OnMenuChar(nChar, nFlags, pMenu);
+ return lResult;
+} // End of OnMenuChar
+#endif
+
+#ifdef BTNST_USE_BCMENU
+void CButtonST::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
+{
+ BOOL bSetFlag = FALSE;
+ if (lpMeasureItemStruct->CtlType == ODT_MENU)
+ {
+ if (IsMenu((HMENU)lpMeasureItemStruct->itemID) && BCMenu::IsMenu((HMENU)lpMeasureItemStruct->itemID))
+ {
+ m_menuPopup.MeasureItem(lpMeasureItemStruct);
+ bSetFlag = TRUE;
+ } // if
+ } // if
+ if (!bSetFlag) CButton::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
+} // End of OnMeasureItem
+#endif
+
+void CButtonST::OnEnable(BOOL bEnable)
+{
+ CButton::OnEnable(bEnable);
+
+ if (bEnable == FALSE)
+ {
+ CWnd* pWnd = GetParent()->GetNextDlgTabItem(this);
+ if (pWnd)
+ pWnd->SetFocus();
+ else
+ GetParent()->SetFocus();
+
+ CancelHover();
+ } // if
+} // End of OnEnable
+
+void CButtonST::OnKillFocus(CWnd * pNewWnd)
+{
+ CButton::OnKillFocus(pNewWnd);
+ CancelHover();
+} // End of OnKillFocus
+
+void CButtonST::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
+{
+ CButton::OnActivate(nState, pWndOther, bMinimized);
+ if (nState == WA_INACTIVE && !marked) CancelHover();
+} // End of OnActivate
+
+void CButtonST::OnCancelMode()
+{
+ CButton::OnCancelMode();
+ CancelHover();
+} // End of OnCancelMode
+
+BOOL CButtonST::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
+{
+ // If a cursor was specified then use it!
+ if (m_hCursor != NULL)
+ {
+ ::SetCursor(m_hCursor);
+ return TRUE;
+ } // if
+
+ return CButton::OnSetCursor(pWnd, nHitTest, message);
+} // End of OnSetCursor
+
+void CButtonST::CancelHover()
+{
+ // Only for flat buttons
+ if (m_bIsFlat)
+ {
+ if (m_bMouseOnButton)
+ {
+ m_bMouseOnButton = FALSE;
+ Invalidate();
+ } // if
+ } // if
+} // End of CancelHover
+
+void CButtonST::OnMouseMove(UINT nFlags, CPoint point)
+{
+ CWnd* wndUnderMouse = NULL;
+ CWnd* wndActive = this;
+ TRACKMOUSEEVENT csTME;
+
+ CButton::OnMouseMove(nFlags, point);
+
+ ClientToScreen(&point);
+ wndUnderMouse = WindowFromPoint(point);
+
+ // If the mouse enter the button with the left button pressed then do nothing
+ if (nFlags & MK_LBUTTON && m_bMouseOnButton == FALSE) return;
+
+ // If our button is not flat then do nothing
+ if (m_bIsFlat == FALSE) return;
+
+ if (m_bAlwaysTrack == FALSE) wndActive = GetActiveWindow();
+
+ if (wndUnderMouse && wndUnderMouse->m_hWnd == m_hWnd && wndActive)
+ {
+ if (!m_bMouseOnButton)
+ {
+ m_bMouseOnButton = TRUE;
+
+ Invalidate();
+
+#ifdef BTNST_USE_SOUND
+ // Play sound ?
+ if (m_csSounds[0].lpszSound)
+ ::PlaySound(m_csSounds[0].lpszSound, m_csSounds[0].hMod, m_csSounds[0].dwFlags);
+#endif
+
+ csTME.cbSize = sizeof(csTME);
+ csTME.dwFlags = TME_LEAVE;
+ csTME.hwndTrack = m_hWnd;
+ ::_TrackMouseEvent(&csTME);
+ } // if
+ } else CancelHover();
+} // End of OnMouseMove
+
+// Handler for WM_MOUSELEAVE
+LRESULT CButtonST::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/)
+{
+ CancelHover();
+ return 0;
+} // End of OnMouseLeave
+
+BOOL CButtonST::OnClicked()
+{
+ SetFocus();
+
+#ifdef BTNST_USE_SOUND
+ // Play sound ?
+ if (m_csSounds[1].lpszSound)
+ ::PlaySound(m_csSounds[1].lpszSound, m_csSounds[1].hMod, m_csSounds[1].dwFlags);
+#endif
+
+ if (m_bIsCheckBox)
+ {
+ m_nCheck = !m_nCheck;
+ Invalidate();
+ } // if
+ else
+ {
+ // Handle the menu (if any)
+#ifdef BTNST_USE_BCMENU
+ if (m_menuPopup.m_hMenu)
+#else
+ if (m_hMenu)
+#endif
+ {
+ CRect rWnd;
+ GetWindowRect(rWnd);
+
+ m_bMenuDisplayed = TRUE;
+ Invalidate();
+
+#ifdef BTNST_USE_BCMENU
+ BCMenu* psub = (BCMenu*)m_menuPopup.GetSubMenu(0);
+ if (m_csCallbacks.hWnd) ::SendMessage(m_csCallbacks.hWnd, m_csCallbacks.nMessage, (WPARAM)psub, m_csCallbacks.lParam);
+ DWORD dwRetValue = psub->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, rWnd.left, rWnd.bottom, this, NULL);
+#else
+ HMENU hSubMenu = ::GetSubMenu(m_hMenu, 0);
+ if (m_csCallbacks.hWnd) ::SendMessage(m_csCallbacks.hWnd, m_csCallbacks.nMessage, (WPARAM)hSubMenu, m_csCallbacks.lParam);
+ DWORD dwRetValue = ::TrackPopupMenuEx(hSubMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, rWnd.left, rWnd.bottom, m_hParentWndMenu, NULL);
+#endif
+
+ m_bMenuDisplayed = FALSE;
+ Invalidate();
+
+ if (dwRetValue)
+ ::PostMessage(m_hParentWndMenu, WM_COMMAND, MAKEWPARAM(dwRetValue, 0), (LPARAM)NULL);
+ } // if
+ else
+ {
+ // Handle the URL (if any)
+ if (_tcslen(m_szURL) > 0)
+ {
+ SHELLEXECUTEINFO csSEI;
+
+ memset(&csSEI, 0, sizeof(csSEI));
+ csSEI.cbSize = sizeof(SHELLEXECUTEINFO);
+ csSEI.fMask = SEE_MASK_FLAG_NO_UI;
+ csSEI.lpVerb = _T("open");
+ csSEI.lpFile = m_szURL;
+ csSEI.nShow = SW_SHOWMAXIMIZED;
+ ::ShellExecuteEx(&csSEI);
+ } // if
+ } // else
+ } // else
+
+ return FALSE;
+} // End of OnClicked
+
+void CButtonST::DrawItem(LPDRAWITEMSTRUCT lpDIS)
+{
+ CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+ CPen* pOldPen;
+
+ // Checkbox?
+ if (m_bIsCheckBox)
+ {
+ m_bIsPressed = ((lpDIS->itemState & ODS_SELECTED) || (m_nCheck != 0) || marked );
+ } // if
+ else // Normal button OR other button style ...
+ {
+ m_bIsPressed = ((lpDIS->itemState & ODS_SELECTED) || marked);
+
+ // If there is a menu and it's displayed, draw the button as pressed
+ if (
+#ifdef BTNST_USE_BCMENU
+ m_menuPopup.m_hMenu
+#else
+ m_hMenu
+#endif
+ && m_bMenuDisplayed) m_bIsPressed = TRUE;
+ } // else
+
+ m_bIsFocused = (lpDIS->itemState & ODS_FOCUS);
+ m_bIsDisabled = (lpDIS->itemState & ODS_DISABLED);
+
+ CRect itemRect = lpDIS->rcItem;
+
+ pDC->SetBkMode(TRANSPARENT);
+
+ if (m_bIsFlat == FALSE)
+ {
+ if (m_bIsFocused || m_bIsDefault)
+ {
+ CBrush br(RGB(0,0,0));
+ pDC->FrameRect(&itemRect, &br);
+ itemRect.DeflateRect(1, 1);
+ } // if
+ } // if
+
+ // Prepare draw... paint button background
+
+ // Draw transparent?
+ if (m_bDrawTransparent)
+ PaintBk(pDC);
+ else
+ OnDrawBackground(pDC, &itemRect);
+
+ // Draw pressed button
+ if (m_bIsPressed)
+ {
+ if (m_bIsFlat)
+ {
+ if (m_bDrawBorder)
+ OnDrawBorder(pDC, &itemRect);
+ }
+ else
+ {
+ CBrush brBtnShadow(GetSysColor(COLOR_BTNSHADOW));
+ pDC->FrameRect(&itemRect, &brBtnShadow);
+ }
+ }
+ else // ...else draw non pressed button
+ {
+ CPen penBtnHiLight(PS_SOLID, 0, GetSysColor(COLOR_BTNHILIGHT)); // White
+ CPen pen3DLight(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT)); // Light gray
+ CPen penBtnShadow(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW)); // Dark gray
+ CPen pen3DDKShadow(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW)); // Black
+
+ if (m_bIsFlat)
+ {
+ if (m_bMouseOnButton && m_bDrawBorder)
+ OnDrawBorder(pDC, &itemRect);
+ }
+ else
+ {
+ // Draw top-left borders
+ // White line
+ pOldPen = pDC->SelectObject(&penBtnHiLight);
+ pDC->MoveTo(itemRect.left, itemRect.bottom-1);
+ pDC->LineTo(itemRect.left, itemRect.top);
+ pDC->LineTo(itemRect.right, itemRect.top);
+ // Light gray line
+ pDC->SelectObject(pen3DLight);
+ pDC->MoveTo(itemRect.left+1, itemRect.bottom-1);
+ pDC->LineTo(itemRect.left+1, itemRect.top+1);
+ pDC->LineTo(itemRect.right, itemRect.top+1);
+ // Draw bottom-right borders
+ // Black line
+ pDC->SelectObject(pen3DDKShadow);
+ pDC->MoveTo(itemRect.left, itemRect.bottom-1);
+ pDC->LineTo(itemRect.right-1, itemRect.bottom-1);
+ pDC->LineTo(itemRect.right-1, itemRect.top-1);
+ // Dark gray line
+ pDC->SelectObject(penBtnShadow);
+ pDC->MoveTo(itemRect.left+1, itemRect.bottom-2);
+ pDC->LineTo(itemRect.right-2, itemRect.bottom-2);
+ pDC->LineTo(itemRect.right-2, itemRect.top);
+ //
+ pDC->SelectObject(pOldPen);
+ } // else
+ } // else
+
+ // Read the button's title
+ CString sTitle;
+ GetWindowText(sTitle);
+
+ CRect captionRect = lpDIS->rcItem;
+
+ // Draw the icon
+ if (m_csIcons[0].hIcon)
+ {
+ DrawTheIcon(pDC, !sTitle.IsEmpty(), &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+ } // if
+
+ if (m_csBitmaps[0].hBitmap)
+ {
+ pDC->SetBkColor(RGB(255,255,255));
+ DrawTheBitmap(pDC, !sTitle.IsEmpty(), &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+ } // if
+
+ // Write the button title (if any)
+ if (sTitle.IsEmpty() == FALSE)
+ {
+ DrawTheText(pDC, (LPCTSTR)sTitle, &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+ } // if
+
+ if (m_bIsFlat == FALSE || (m_bIsFlat && m_bDrawFlatFocus))
+ {
+ // Draw the focus rect
+ if (m_bIsFocused)
+ {
+ CRect focusRect = itemRect;
+ if (!m_bIsFlat)
+ focusRect.DeflateRect(3, 3);
+ pDC->DrawFocusRect(&focusRect);
+ } // if
+ } // if
+} // End of DrawItem
+
+void CButtonST::PaintBk(CDC* pDC)
+{
+ CClientDC clDC(GetParent());
+ CRect rect;
+ CRect rect1;
+
+ GetClientRect(rect);
+
+ GetWindowRect(rect1);
+ GetParent()->ScreenToClient(rect1);
+
+ if (m_dcBk.m_hDC == NULL)
+ {
+ m_dcBk.CreateCompatibleDC(&clDC);
+ m_bmpBk.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());
+ m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
+ m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY);
+ } // if
+
+ pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY);
+} // End of PaintBk
+
+HBITMAP CButtonST::CreateBitmapMask(HBITMAP hSourceBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTransColor)
+{
+ HBITMAP hMask = NULL;
+ HDC hdcSrc = NULL;
+ HDC hdcDest = NULL;
+ HBITMAP hbmSrcT = NULL;
+ HBITMAP hbmDestT = NULL;
+ COLORREF crSaveBk;
+ COLORREF crSaveDestText;
+
+ hMask = ::CreateBitmap(dwWidth, dwHeight, 1, 1, NULL);
+ if (hMask == NULL) return NULL;
+
+ hdcSrc = ::CreateCompatibleDC(NULL);
+ hdcDest = ::CreateCompatibleDC(NULL);
+
+ hbmSrcT = (HBITMAP)::SelectObject(hdcSrc, hSourceBitmap);
+ hbmDestT = (HBITMAP)::SelectObject(hdcDest, hMask);
+
+ crSaveBk = ::SetBkColor(hdcSrc, crTransColor);
+
+ ::BitBlt(hdcDest, 0, 0, dwWidth, dwHeight, hdcSrc, 0, 0, SRCCOPY);
+
+ crSaveDestText = ::SetTextColor(hdcSrc, RGB(255, 255, 255));
+ ::SetBkColor(hdcSrc,RGB(0, 0, 0));
+
+ ::BitBlt(hdcSrc, 0, 0, dwWidth, dwHeight, hdcDest, 0, 0, SRCAND);
+
+ SetTextColor(hdcDest, crSaveDestText);
+
+ ::SetBkColor(hdcSrc, crSaveBk);
+ ::SelectObject(hdcSrc, hbmSrcT);
+ ::SelectObject(hdcDest, hbmDestT);
+
+ ::DeleteDC(hdcSrc);
+ ::DeleteDC(hdcDest);
+
+ return hMask;
+} // End of CreateBitmapMask
+
+//
+// Parameters:
+// [IN] bHasTitle
+// TRUE if the button has a text
+// [IN] rpItem
+// A pointer to a RECT structure indicating the allowed paint area
+// [IN/OUT]rpTitle
+// A pointer to a CRect object indicating the paint area reserved for the
+// text. This structure will be modified if necessary.
+// [IN] bIsPressed
+// TRUE if the button is currently pressed
+// [IN] dwWidth
+// Width of the image (icon or bitmap)
+// [IN] dwHeight
+// Height of the image (icon or bitmap)
+// [OUT] rpImage
+// A pointer to a CRect object that will receive the area available to the image
+//
+void CButtonST::PrepareImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage)
+{
+ CRect rBtn;
+
+ rpImage->CopyRect(rpItem);
+
+ switch (m_byAlign)
+ {
+ case ST_ALIGN_HORIZ:
+ if (bHasTitle == FALSE)
+ {
+ // Center image horizontally
+ rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+ }
+ else
+ {
+ // Image must be placed just inside the focus rect
+ rpImage->left += m_ptImageOrg.x;
+ rpTitle->left += dwWidth + m_ptImageOrg.x;
+ }
+ // Center image vertically
+ rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
+ break;
+
+ case ST_ALIGN_HORIZ_RIGHT:
+ GetClientRect(&rBtn);
+ if (bHasTitle == FALSE)
+ {
+ // Center image horizontally
+ rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+ }
+ else
+ {
+ // Image must be placed just inside the focus rect
+ rpTitle->right = rpTitle->Width() - dwWidth - m_ptImageOrg.x;
+ rpTitle->left = m_ptImageOrg.x;
+ rpImage->left = rBtn.right - dwWidth - m_ptImageOrg.x;
+ // Center image vertically
+ rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
+ }
+ break;
+
+ case ST_ALIGN_VERT:
+ // Center image horizontally
+ rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+ if (bHasTitle == FALSE)
+ {
+ // Center image vertically
+ rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
+ }
+ else
+ {
+ rpImage->top = m_ptImageOrg.y;
+ rpTitle->top += dwHeight;
+ }
+ break;
+
+ case ST_ALIGN_OVERLAP:
+ break;
+ } // switch
+
+ // If button is pressed then press image also
+ if (bIsPressed && m_bIsCheckBox == FALSE)
+ rpImage->OffsetRect(m_ptPressedOffset.x, m_ptPressedOffset.y);
+} // End of PrepareImageRect
+
+void CButtonST::DrawTheIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+ BYTE byIndex = 0;
+
+ // Select the icon to use
+ if ((m_bIsCheckBox && bIsPressed) || (!m_bIsCheckBox && (bIsPressed || m_bMouseOnButton)))
+ byIndex = 0;
+ else
+ byIndex = (m_csIcons[1].hIcon == NULL ? 0 : 1);
+
+ CRect rImage;
+ PrepareImageRect(bHasTitle, rpItem, rpCaption, bIsPressed, m_csIcons[byIndex].dwWidth, m_csIcons[byIndex].dwHeight, &rImage);
+
+ // Ole'!
+ pDC->DrawState( rImage.TopLeft(),
+ rImage.Size(),
+ m_csIcons[byIndex].hIcon,
+ (bIsDisabled ? DSS_DISABLED : DSS_NORMAL),
+ (CBrush*)NULL);
+} // End of DrawTheIcon
+
+void CButtonST::DrawTheBitmap(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+ HDC hdcBmpMem = NULL;
+ HBITMAP hbmOldBmp = NULL;
+ HDC hdcMem = NULL;
+ HBITMAP hbmT = NULL;
+
+ BYTE byIndex = 0;
+
+ // Select the bitmap to use
+ if ((m_bIsCheckBox && bIsPressed) || (!m_bIsCheckBox && (bIsPressed || m_bMouseOnButton)))
+ byIndex = 0;
+ else
+ byIndex = (m_csBitmaps[1].hBitmap == NULL ? 0 : 1);
+
+ CRect rImage;
+ PrepareImageRect(bHasTitle, rpItem, rpCaption, bIsPressed, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, &rImage);
+
+ hdcBmpMem = ::CreateCompatibleDC(pDC->m_hDC);
+
+ hbmOldBmp = (HBITMAP)::SelectObject(hdcBmpMem, m_csBitmaps[byIndex].hBitmap);
+
+ hdcMem = ::CreateCompatibleDC(NULL);
+
+ hbmT = (HBITMAP)::SelectObject(hdcMem, m_csBitmaps[byIndex].hMask);
+
+ if (bIsDisabled && m_bShowDisabledBitmap)
+ {
+ HDC hDC = NULL;
+ HBITMAP hBitmap = NULL;
+
+ hDC = ::CreateCompatibleDC(pDC->m_hDC);
+ hBitmap = ::CreateCompatibleBitmap(pDC->m_hDC, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight);
+ HBITMAP hOldBmp2 = (HBITMAP)::SelectObject(hDC, hBitmap);
+
+ RECT rRect;
+ rRect.left = 0;
+ rRect.top = 0;
+ rRect.right = rImage.right + 1;
+ rRect.bottom = rImage.bottom + 1;
+ ::FillRect(hDC, &rRect, (HBRUSH)RGB(255, 255, 255));
+
+ COLORREF crOldColor = ::SetBkColor(hDC, RGB(255,255,255));
+
+ ::BitBlt(hDC, 0, 0, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcMem, 0, 0, SRCAND);
+ ::BitBlt(hDC, 0, 0, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcBmpMem, 0, 0, SRCPAINT);
+
+ ::SetBkColor(hDC, crOldColor);
+ ::SelectObject(hDC, hOldBmp2);
+ ::DeleteDC(hDC);
+
+ pDC->DrawState( CPoint(rImage.left/*+1*/, rImage.top),
+ CSize(m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight),
+ hBitmap, DST_BITMAP | DSS_DISABLED);
+
+ VERIFY( ::DeleteObject(hBitmap) );
+ } // if
+ else
+ {
+ ::BitBlt(pDC->m_hDC, rImage.left, rImage.top, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcMem, 0, 0, SRCAND);
+
+ ::BitBlt(pDC->m_hDC, rImage.left, rImage.top, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcBmpMem, 0, 0, SRCPAINT);
+ } // else
+
+ ::SelectObject(hdcMem, hbmT);
+ ::DeleteDC(hdcMem);
+
+ ::SelectObject(hdcBmpMem, hbmOldBmp);
+ ::DeleteDC(hdcBmpMem);
+} // End of DrawTheBitmap
+
+void CButtonST::DrawTheText(CDC* pDC, LPCTSTR lpszText, RECT* /*rpItem*/, CRect* rpCaption, BOOL /*bIsPressed*/, BOOL /*bIsDisabled*/)
+{
+ // Draw the button's title
+ // If button is pressed then "press" title also
+ if (m_bIsPressed && m_bIsCheckBox == FALSE)
+ rpCaption->OffsetRect(m_ptPressedOffset.x, m_ptPressedOffset.y);
+
+ // ONLY FOR DEBUG
+ //CBrush brBtnShadow(RGB(255, 0, 0));
+ //pDC->FrameRect(rCaption, &brBtnShadow);
+
+ // Center text
+ CRect centerRect = rpCaption;
+ pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER | DT_CALCRECT);
+
+ if (!leftAligned) rpCaption->OffsetRect((centerRect.Width() - rpCaption->Width())/2, (centerRect.Height() - rpCaption->Height())/2);
+ else rpCaption->OffsetRect( 8, (centerRect.Height() - rpCaption->Height())/2);
+
+ /* RFU
+ rpCaption->OffsetRect(0, (centerRect.Height() - rpCaption->Height())/2);
+ rpCaption->OffsetRect((centerRect.Width() - rpCaption->Width())-4, (centerRect.Height() - rpCaption->Height())/2);
+ */
+
+ pDC->SetBkMode(TRANSPARENT);
+ /*
+ pDC->DrawState(rCaption->TopLeft(), rCaption->Size(), (LPCTSTR)sTitle, (bIsDisabled ? DSS_DISABLED : DSS_NORMAL),
+ TRUE, 0, (CBrush*)NULL);
+ */
+ if (m_bIsDisabled)
+ {
+ rpCaption->OffsetRect(1, 1);
+ pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
+ pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+ rpCaption->OffsetRect(-1, -1);
+ pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
+ pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+ } // if
+ else
+ {
+ if (m_bMouseOnButton || m_bIsPressed)
+ {
+ pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_IN]);
+ pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_IN]);
+ } // if
+ else
+ {
+ pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_OUT]);
+ pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_OUT]);
+ } // else
+ pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+ } // if
+} // End of DrawTheText
+
+// This function creates a grayscale icon starting from a given icon.
+// The resulting icon will have the same size of the original one.
+//
+// Parameters:
+// [IN] hIcon
+// Handle to the original icon.
+//
+// Return value:
+// If the function succeeds, the return value is the handle to the newly created
+// grayscale icon.
+// If the function fails, the return value is NULL.
+//
+// Updates:
+// 03/May/2002 Removed dependancy from m_hWnd
+// Removed 1 BitBlt operation
+//
+HICON CButtonST::CreateGrayscaleIcon(HICON hIcon)
+{
+ HICON hGrayIcon = NULL;
+ HDC hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+ BITMAP bmp;
+ HBITMAP hOldBmp1 = NULL, hOldBmp2 = NULL;
+ ICONINFO csII, csGrayII;
+ BOOL bRetValue = FALSE;
+
+ bRetValue = ::GetIconInfo(hIcon, &csII);
+ if (bRetValue == FALSE) return NULL;
+
+ hMainDC = ::GetDC(HWND_DESKTOP);
+ hMemDC1 = ::CreateCompatibleDC(hMainDC);
+ hMemDC2 = ::CreateCompatibleDC(hMainDC);
+ if (hMainDC == NULL || hMemDC1 == NULL || hMemDC2 == NULL) return NULL;
+
+ if (::GetObject(csII.hbmColor, sizeof(BITMAP), &bmp))
+ {
+ DWORD dwWidth = csII.xHotspot*2;
+ DWORD dwHeight = csII.yHotspot*2;
+
+ csGrayII.hbmColor = ::CreateBitmap(dwWidth, dwHeight, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
+ if (csGrayII.hbmColor)
+ {
+ hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, csII.hbmColor);
+ hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, csGrayII.hbmColor);
+
+ //::BitBlt(hMemDC2, 0, 0, dwWidth, dwHeight, hMemDC1, 0, 0, SRCCOPY);
+
+ DWORD dwLoopY = 0, dwLoopX = 0;
+ COLORREF crPixel = 0;
+ BYTE byNewPixel = 0;
+
+ for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+ {
+ for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+ {
+ crPixel = ::GetPixel(hMemDC1, dwLoopX, dwLoopY);
+
+ byNewPixel = (BYTE)((GetRValue(crPixel) * 0.299) + (GetGValue(crPixel) * 0.587) + (GetBValue(crPixel) * 0.114));
+ if (crPixel) ::SetPixel(hMemDC2, dwLoopX, dwLoopY, RGB(byNewPixel, byNewPixel, byNewPixel));
+ } // for
+ } // for
+
+ ::SelectObject(hMemDC1, hOldBmp1);
+ ::SelectObject(hMemDC2, hOldBmp2);
+
+ csGrayII.hbmMask = csII.hbmMask;
+
+ csGrayII.fIcon = TRUE;
+ hGrayIcon = ::CreateIconIndirect(&csGrayII);
+ } // if
+
+ VERIFY( ::DeleteObject(csGrayII.hbmColor) );
+ //::DeleteObject(csGrayII.hbmMask);
+ } // if
+
+ VERIFY( ::DeleteObject(csII.hbmColor) );
+ VERIFY( ::DeleteObject(csII.hbmMask) );
+ VERIFY( ::DeleteDC(hMemDC1) );
+ VERIFY( ::DeleteDC(hMemDC2) );
+ ::ReleaseDC(NULL, hMainDC);
+
+ return hGrayIcon;
+} // End of CreateGrayscaleIcon
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+// [IN] nIconIn
+// ID number of the icon resource to show when the mouse is over the button.
+// Pass NULL to remove any icon from the button.
+// [IN] nIconOut
+// ID number of the icon resource to show when the mouse is outside the button.
+// Can be NULL.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDRESOURCE
+// Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(LPCTSTR pszIconIn, LPCTSTR pszIconOut)
+{
+ HICON hIconIn = NULL;
+ HICON hIconOut = NULL;
+
+ // Set icon when the mouse is IN the button
+ hIconIn = theApp.LoadIcon(pszIconIn, 16, 16);
+
+ // Set icon when the mouse is OUT the button
+ if (pszIconOut)
+ {
+ if ((UINT)pszIconOut == (UINT)BTNST_AUTO_GRAY)
+ hIconOut = BTNST_AUTO_GRAY;
+ else
+ hIconOut = theApp.LoadIcon(pszIconOut, 16, 16);
+ } // if
+
+ return SetIcon(hIconIn, hIconOut);
+} // End of SetIcon
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+// [IN] hIconIn
+// Handle fo the icon to show when the mouse is over the button.
+// Pass NULL to remove any icon from the button.
+// [IN] hIconOut
+// Handle to the icon to show when the mouse is outside the button.
+// Can be NULL.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDRESOURCE
+// Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(HICON hIconIn, HICON hIconOut)
+{
+ BOOL bRetValue;
+ ICONINFO ii;
+
+ // Free any loaded resource
+ FreeResources();
+
+ if (hIconIn)
+ {
+ // Icon when mouse over button?
+ m_csIcons[0].hIcon = hIconIn;
+ // Get icon dimension
+ ::ZeroMemory(&ii, sizeof(ICONINFO));
+ bRetValue = ::GetIconInfo(hIconIn, &ii);
+ if (bRetValue == FALSE)
+ {
+ FreeResources();
+ return BTNST_INVALIDRESOURCE;
+ } // if
+
+ m_csIcons[0].dwWidth = (DWORD)(ii.xHotspot * 2);
+ m_csIcons[0].dwHeight = (DWORD)(ii.yHotspot * 2);
+ VERIFY( ::DeleteObject(ii.hbmMask) );
+ VERIFY( ::DeleteObject(ii.hbmColor) );
+
+ // Icon when mouse outside button?
+ if (hIconOut)
+ {
+ if (hIconOut == BTNST_AUTO_GRAY)
+ {
+ hIconOut = CreateGrayscaleIcon(hIconIn);
+ } // if
+
+ m_csIcons[1].hIcon = hIconOut;
+ // Get icon dimension
+ ::ZeroMemory(&ii, sizeof(ICONINFO));
+ bRetValue = ::GetIconInfo(hIconOut, &ii);
+ if (bRetValue == FALSE)
+ {
+ FreeResources();
+ return BTNST_INVALIDRESOURCE;
+ } // if
+
+ m_csIcons[1].dwWidth = (DWORD)(ii.xHotspot * 2);
+ m_csIcons[1].dwHeight = (DWORD)(ii.yHotspot * 2);
+ VERIFY( ::DeleteObject(ii.hbmMask) );
+ VERIFY( ::DeleteObject(ii.hbmColor) );
+ } // if
+ } // if
+
+ Invalidate();
+
+ return BTNST_OK;
+} // End of SetIcon
+
+// This function assigns bitmaps to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+// [IN] nBitmapIn
+// ID number of the bitmap resource to show when the mouse is over the button.
+// Pass NULL to remove any bitmap from the button.
+// [IN] crTransColorIn
+// Color (inside nBitmapIn) to be used as transparent color.
+// [IN] nBitmapOut
+// ID number of the bitmap resource to show when the mouse is outside the button.
+// Can be NULL.
+// [IN] crTransColorOut
+// Color (inside nBitmapOut) to be used as transparent color.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDRESOURCE
+// Failed loading the specified resource.
+// BTNST_FAILEDMASK
+// Failed creating mask bitmap.
+//
+DWORD CButtonST::SetBitmaps(int nBitmapIn, COLORREF crTransColorIn, int nBitmapOut, COLORREF crTransColorOut)
+{
+ HBITMAP hBitmapIn = NULL;
+ HBITMAP hBitmapOut = NULL;
+ HINSTANCE hInstResource = NULL;
+
+ // Find correct resource handle
+ hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nBitmapIn), RT_BITMAP);
+
+ // Load bitmap In
+ hBitmapIn = (HBITMAP)::LoadImage(hInstResource, MAKEINTRESOURCE(nBitmapIn), IMAGE_BITMAP, 0, 0, 0);
+
+ // Load bitmap Out
+ if (nBitmapOut)
+ hBitmapOut = (HBITMAP)::LoadImage(hInstResource, MAKEINTRESOURCE(nBitmapOut), IMAGE_BITMAP, 0, 0, 0);
+
+ return SetBitmaps(hBitmapIn, crTransColorIn, hBitmapOut, crTransColorOut);
+} // End of SetBitmaps
+
+// This function assigns bitmaps to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+// [IN] hBitmapIn
+// Handle fo the bitmap to show when the mouse is over the button.
+// Pass NULL to remove any bitmap from the button.
+// [IN] crTransColorIn
+// Color (inside hBitmapIn) to be used as transparent color.
+// [IN] hBitmapOut
+// Handle to the bitmap to show when the mouse is outside the button.
+// Can be NULL.
+// [IN] crTransColorOut
+// Color (inside hBitmapOut) to be used as transparent color.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDRESOURCE
+// Failed loading the specified resource.
+// BTNST_FAILEDMASK
+// Failed creating mask bitmap.
+//
+DWORD CButtonST::SetBitmaps(HBITMAP hBitmapIn, COLORREF crTransColorIn, HBITMAP hBitmapOut, COLORREF crTransColorOut)
+{
+ int nRetValue;
+ BITMAP csBitmapSize;
+
+ // Free any loaded resource
+ FreeResources();
+
+ if (hBitmapIn)
+ {
+ m_csBitmaps[0].hBitmap = hBitmapIn;
+ m_csBitmaps[0].crTransparent = crTransColorIn;
+ // Get bitmap size
+ nRetValue = ::GetObject(hBitmapIn, sizeof(csBitmapSize), &csBitmapSize);
+ if (nRetValue == 0)
+ {
+ FreeResources();
+ return BTNST_INVALIDRESOURCE;
+ } // if
+ m_csBitmaps[0].dwWidth = (DWORD)csBitmapSize.bmWidth;
+ m_csBitmaps[0].dwHeight = (DWORD)csBitmapSize.bmHeight;
+
+ // Create mask for bitmap In
+ m_csBitmaps[0].hMask = CreateBitmapMask(hBitmapIn, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight, crTransColorIn);
+ if (m_csBitmaps[0].hMask == NULL)
+ {
+ FreeResources();
+ return BTNST_FAILEDMASK;
+ } // if
+
+ if (hBitmapOut)
+ {
+ m_csBitmaps[1].hBitmap = hBitmapOut;
+ m_csBitmaps[1].crTransparent = crTransColorOut;
+ // Get bitmap size
+ nRetValue = ::GetObject(hBitmapOut, sizeof(csBitmapSize), &csBitmapSize);
+ if (nRetValue == 0)
+ {
+ FreeResources();
+ return BTNST_INVALIDRESOURCE;
+ } // if
+ m_csBitmaps[1].dwWidth = (DWORD)csBitmapSize.bmWidth;
+ m_csBitmaps[1].dwHeight = (DWORD)csBitmapSize.bmHeight;
+
+ // Create mask for bitmap Out
+ m_csBitmaps[1].hMask = CreateBitmapMask(hBitmapOut, m_csBitmaps[1].dwWidth, m_csBitmaps[1].dwHeight, crTransColorOut);
+ if (m_csBitmaps[1].hMask == NULL)
+ {
+ FreeResources();
+ return BTNST_FAILEDMASK;
+ } // if
+ } // if
+ } // if
+
+ Invalidate();
+
+ return BTNST_OK;
+} // End of SetBitmaps
+
+// This functions sets the button to have a standard or flat style.
+//
+// Parameters:
+// [IN] bFlat
+// If TRUE the button will have a flat style, else
+// will have a standard style.
+// By default, CButtonST buttons are flat.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::SetFlat(BOOL bFlat, BOOL bRepaint)
+{
+ m_bIsFlat = bFlat;
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of SetFlat
+
+// This function sets the alignment type between icon/bitmap and text.
+//
+// Parameters:
+// [IN] byAlign
+// Alignment type. Can be one of the following values:
+// ST_ALIGN_HORIZ Icon/bitmap on the left, text on the right
+// ST_ALIGN_VERT Icon/bitmap on the top, text on the bottom
+// ST_ALIGN_HORIZ_RIGHT Icon/bitmap on the right, text on the left
+// ST_ALIGN_OVERLAP Icon/bitmap on the same space as text
+// By default, CButtonST buttons have ST_ALIGN_HORIZ alignment.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDALIGN
+// Alignment type not supported.
+//
+DWORD CButtonST::SetAlign(BYTE byAlign, BOOL bRepaint)
+{
+ switch (byAlign)
+ {
+ case ST_ALIGN_HORIZ:
+ case ST_ALIGN_HORIZ_RIGHT:
+ case ST_ALIGN_VERT:
+ case ST_ALIGN_OVERLAP:
+ m_byAlign = byAlign;
+ if (bRepaint) Invalidate();
+ return BTNST_OK;
+ break;
+ } // switch
+
+ return BTNST_INVALIDALIGN;
+} // End of SetAlign
+
+// This function sets the pressed style.
+//
+// Parameters:
+// [IN] byStyle
+// Pressed style. Can be one of the following values:
+// BTNST_PRESSED_LEFTRIGHT Pressed style from left to right (as usual)
+// BTNST_PRESSED_TOPBOTTOM Pressed style from top to bottom
+// By default, CButtonST buttons have BTNST_PRESSED_LEFTRIGHT style.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDPRESSEDSTYLE
+// Pressed style not supported.
+//
+DWORD CButtonST::SetPressedStyle(BYTE byStyle, BOOL bRepaint)
+{
+ switch (byStyle)
+ {
+ case BTNST_PRESSED_LEFTRIGHT:
+ m_ptPressedOffset.x = 1;
+ m_ptPressedOffset.y = 1;
+ break;
+ case BTNST_PRESSED_TOPBOTTOM:
+ m_ptPressedOffset.x = 0;
+ m_ptPressedOffset.y = 2;
+ break;
+ default:
+ return BTNST_INVALIDPRESSEDSTYLE;
+ } // switch
+
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of SetPressedStyle
+
+// This function sets the state of the checkbox.
+// If the button is not a checkbox, this function has no meaning.
+//
+// Parameters:
+// [IN] nCheck
+// 1 to check the checkbox.
+// 0 to un-check the checkbox.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::SetCheck(int nCheck, BOOL bRepaint)
+{
+ if (m_bIsCheckBox)
+ {
+ if (nCheck == 0) m_nCheck = 0;
+ else m_nCheck = 1;
+
+ if (bRepaint) Invalidate();
+ } // if
+
+ return BTNST_OK;
+} // End of SetCheck
+
+// This function returns the current state of the checkbox.
+// If the button is not a checkbox, this function has no meaning.
+//
+// Return value:
+// The current state of the checkbox.
+// 1 if checked.
+// 0 if not checked or the button is not a checkbox.
+//
+int CButtonST::GetCheck()
+{
+ return m_nCheck;
+} // End of GetCheck
+
+// This function sets all colors to a default value.
+//
+// Parameters:
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::SetDefaultColors(BOOL bRepaint)
+{
+ m_crColors[BTNST_COLOR_BK_IN] = ::GetSysColor(COLOR_BTNFACE);
+ m_crColors[BTNST_COLOR_FG_IN] = ::GetSysColor(COLOR_BTNTEXT);
+ m_crColors[BTNST_COLOR_BK_OUT] = ::GetSysColor(COLOR_BTNFACE);
+ m_crColors[BTNST_COLOR_FG_OUT] = ::GetSysColor(COLOR_BTNTEXT);
+ m_crColors[BTNST_COLOR_BK_FOCUS] = ::GetSysColor(COLOR_BTNFACE);
+ m_crColors[BTNST_COLOR_FG_FOCUS] = ::GetSysColor(COLOR_BTNTEXT);
+
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of SetDefaultColors
+
+// This function sets the color to use for a particular state.
+//
+// Parameters:
+// [IN] byColorIndex
+// Index of the color to set. Can be one of the following values:
+// BTNST_COLOR_BK_IN Background color when mouse is over the button
+// BTNST_COLOR_FG_IN Text color when mouse is over the button
+// BTNST_COLOR_BK_OUT Background color when mouse is outside the button
+// BTNST_COLOR_FG_OUT Text color when mouse is outside the button
+// BTNST_COLOR_BK_FOCUS Background color when the button is focused
+// BTNST_COLOR_FG_FOCUS Text color when the button is focused
+// [IN] crColor
+// New color.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDINDEX
+// Invalid color index.
+//
+DWORD CButtonST::SetColor(BYTE byColorIndex, COLORREF crColor, BOOL bRepaint)
+{
+ if (byColorIndex >= BTNST_MAX_COLORS) return BTNST_INVALIDINDEX;
+
+ // Set new color
+ m_crColors[byColorIndex] = crColor;
+
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of SetColor
+
+// This functions returns the color used for a particular state.
+//
+// Parameters:
+// [IN] byColorIndex
+// Index of the color to get.
+// See SetColor for the list of available colors.
+// [OUT] crpColor
+// A pointer to a COLORREF that will receive the color.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDINDEX
+// Invalid color index.
+//
+DWORD CButtonST::GetColor(BYTE byColorIndex, COLORREF* crpColor)
+{
+ if (byColorIndex >= BTNST_MAX_COLORS) return BTNST_INVALIDINDEX;
+
+ // Get color
+ *crpColor = m_crColors[byColorIndex];
+
+ return BTNST_OK;
+} // End of GetColor
+
+// This function applies an offset to the RGB components of the specified color.
+// This function can be seen as an easy way to make a color darker or lighter than
+// its default value.
+//
+// Parameters:
+// [IN] byColorIndex
+// Index of the color to set.
+// See SetColor for the list of available colors.
+// [IN] shOffsetColor
+// A short value indicating the offset to apply to the color.
+// This value must be between -255 and 255.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDINDEX
+// Invalid color index.
+// BTNST_BADPARAM
+// The specified offset is out of range.
+//
+DWORD CButtonST::OffsetColor(BYTE byColorIndex, short shOffset, BOOL bRepaint)
+{
+ BYTE byRed = 0;
+ BYTE byGreen = 0;
+ BYTE byBlue = 0;
+ short shOffsetR = shOffset;
+ short shOffsetG = shOffset;
+ short shOffsetB = shOffset;
+
+ if (byColorIndex >= BTNST_MAX_COLORS) return BTNST_INVALIDINDEX;
+ if (shOffset < -255 || shOffset > 255) return BTNST_BADPARAM;
+
+ // Get RGB components of specified color
+ byRed = GetRValue(m_crColors[byColorIndex]);
+ byGreen = GetGValue(m_crColors[byColorIndex]);
+ byBlue = GetBValue(m_crColors[byColorIndex]);
+
+ // Calculate max. allowed real offset
+ if (shOffset > 0)
+ {
+ if (byRed + shOffset > 255) shOffsetR = 255 - byRed;
+ if (byGreen + shOffset > 255) shOffsetG = 255 - byGreen;
+ if (byBlue + shOffset > 255) shOffsetB = 255 - byBlue;
+
+ shOffset = min(min(shOffsetR, shOffsetG), shOffsetB);
+ } // if
+ else
+ {
+ if (byRed + shOffset < 0) shOffsetR = -byRed;
+ if (byGreen + shOffset < 0) shOffsetG = -byGreen;
+ if (byBlue + shOffset < 0) shOffsetB = -byBlue;
+
+ shOffset = max(max(shOffsetR, shOffsetG), shOffsetB);
+ } // else
+
+ // Set new color
+ m_crColors[byColorIndex] = RGB(byRed + shOffset, byGreen + shOffset, byBlue + shOffset);
+
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of OffsetColor
+
+// This function sets the hilight logic for the button.
+// Applies only to flat buttons.
+//
+// Parameters:
+// [IN] bAlwaysTrack
+// If TRUE the button will be hilighted even if the window that owns it, is
+// not the active window.
+// If FALSE the button will be hilighted only if the window that owns it,
+// is the active window.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::SetAlwaysTrack(BOOL bAlwaysTrack)
+{
+ m_bAlwaysTrack = bAlwaysTrack;
+ return BTNST_OK;
+} // End of SetAlwaysTrack
+
+// This function sets the cursor to be used when the mouse is over the button.
+//
+// Parameters:
+// [IN] nCursorId
+// ID number of the cursor resource.
+// Pass NULL to remove a previously loaded cursor.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDRESOURCE
+// Failed loading the specified resource.
+//
+DWORD CButtonST::SetBtnCursor(int nCursorId, BOOL bRepaint)
+{
+ HINSTANCE hInstResource = NULL;
+ // Destroy any previous cursor
+ if (m_hCursor)
+ {
+ ::DestroyCursor(m_hCursor);
+ m_hCursor = NULL;
+ } // if
+
+ // Load cursor
+ if (nCursorId)
+ {
+ hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nCursorId), RT_GROUP_CURSOR);
+ // Load cursor resource
+ m_hCursor = (HCURSOR)::LoadImage(hInstResource, MAKEINTRESOURCE(nCursorId), IMAGE_CURSOR, 0, 0, 0);
+ // Repaint the button
+ if (bRepaint) Invalidate();
+ // If something wrong
+ if (m_hCursor == NULL) return BTNST_INVALIDRESOURCE;
+ } // if
+
+ return BTNST_OK;
+} // End of SetBtnCursor
+
+// This function sets if the button border must be drawn.
+// Applies only to flat buttons.
+//
+// Parameters:
+// [IN] bDrawBorder
+// If TRUE the border will be drawn.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::DrawBorder(BOOL bDrawBorder, BOOL bRepaint)
+{
+ m_bDrawBorder = bDrawBorder;
+ // Repaint the button
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of DrawBorder
+
+// This function sets if the focus rectangle must be drawn for flat buttons.
+//
+// Parameters:
+// [IN] bDrawFlatFocus
+// If TRUE the focus rectangle will be drawn also for flat buttons.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::DrawFlatFocus(BOOL bDrawFlatFocus, BOOL bRepaint)
+{
+ m_bDrawFlatFocus = bDrawFlatFocus;
+ // Repaint the button
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of DrawFlatFocus
+
+void CButtonST::InitToolTip()
+{
+ if (m_ToolTip.m_hWnd == NULL)
+ {
+ // Create ToolTip control
+ m_ToolTip.Create(this);
+ // Create inactive
+ m_ToolTip.Activate(FALSE);
+ // Enable multiline
+ m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, 400);
+ } // if
+} // End of InitToolTip
+
+// This function sets the text to show in the button tooltip.
+//
+// Parameters:
+// [IN] nText
+// ID number of the string resource containing the text to show.
+// [IN] bActivate
+// If TRUE the tooltip will be created active.
+//
+void CButtonST::SetTooltipText(int nText, BOOL bActivate)
+{
+ CString sText;
+
+ // Load string resource
+ sText.LoadString(nText);
+ // If string resource is not empty
+ if (sText.IsEmpty() == FALSE) SetTooltipText((LPCTSTR)sText, bActivate);
+} // End of SetTooltipText
+
+// This function sets the text to show in the button tooltip.
+//
+// Parameters:
+// [IN] lpszText
+// Pointer to a null-terminated string containing the text to show.
+// [IN] bActivate
+// If TRUE the tooltip will be created active.
+//
+void CButtonST::SetTooltipText(LPCTSTR lpszText, BOOL bActivate)
+{
+ // We cannot accept NULL pointer
+ if (lpszText == NULL) return;
+
+ // Initialize ToolTip
+ InitToolTip();
+
+ // If there is no tooltip defined then add it
+ if (m_ToolTip.GetToolCount() == 0)
+ {
+ CRect rectBtn;
+ GetClientRect(rectBtn);
+ m_ToolTip.AddTool(this, lpszText, rectBtn, 1);
+ } // if
+
+ // Set text for tooltip
+ m_ToolTip.UpdateTipText(lpszText, this, 1);
+ m_ToolTip.Activate(bActivate);
+} // End of SetTooltipText
+
+// This function enables or disables the button tooltip.
+//
+// Parameters:
+// [IN] bActivate
+// If TRUE the tooltip will be activated.
+//
+void CButtonST::ActivateTooltip(BOOL bActivate)
+{
+ // If there is no tooltip then do nothing
+ if (m_ToolTip.GetToolCount() == 0) return;
+
+ // Activate tooltip
+ m_ToolTip.Activate(bActivate);
+} // End of EnableTooltip
+
+// This function returns if the button is the default button.
+//
+// Return value:
+// TRUE
+// The button is the default button.
+// FALSE
+// The button is not the default button.
+//
+BOOL CButtonST::GetDefault()
+{
+ return m_bIsDefault;
+} // End of GetDefault
+
+// This function enables the transparent mode.
+// Note: this operation is not reversible.
+// DrawTransparent should be called just after the button is created.
+// Do not use trasparent buttons until you really need it (you have a bitmapped
+// background) since each transparent button makes a copy in memory of its background.
+// This may bring unnecessary memory use and execution overload.
+//
+// Parameters:
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+void CButtonST::DrawTransparent(BOOL bRepaint)
+{
+ m_bDrawTransparent = TRUE;
+
+ // Restore old bitmap (if any)
+ if (m_dcBk.m_hDC != NULL && m_pbmpOldBk != NULL)
+ {
+ m_dcBk.SelectObject(m_pbmpOldBk);
+ } // if
+
+ m_bmpBk.DeleteObject();
+ m_dcBk.DeleteDC();
+
+ // Repaint the button
+ if (bRepaint) Invalidate();
+} // End of DrawTransparent
+
+DWORD CButtonST::SetBk(CDC* pDC)
+{
+ if (m_bDrawTransparent && pDC)
+ {
+ // Restore old bitmap (if any)
+ if (m_dcBk.m_hDC != NULL && m_pbmpOldBk != NULL)
+ {
+ m_dcBk.SelectObject(m_pbmpOldBk);
+ } // if
+
+ m_bmpBk.DeleteObject();
+ m_dcBk.DeleteDC();
+
+ CRect rect;
+ CRect rect1;
+
+ GetClientRect(rect);
+
+ GetWindowRect(rect1);
+ GetParent()->ScreenToClient(rect1);
+
+ m_dcBk.CreateCompatibleDC(pDC);
+ m_bmpBk.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
+ m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
+ m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rect1.left, rect1.top, SRCCOPY);
+
+ return BTNST_OK;
+ } // if
+
+ return BTNST_BADPARAM;
+} // End of SetBk
+
+// This function sets the URL that will be opened when the button is clicked.
+//
+// Parameters:
+// [IN] lpszURL
+// Pointer to a null-terminated string that contains the URL.
+// Pass NULL to removed any previously specified URL.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::SetURL(LPCTSTR lpszURL)
+{
+ // Remove any existing URL
+ memset(m_szURL, 0, sizeof(m_szURL));
+
+ if (lpszURL)
+ {
+ // Store the URL
+ _tcsncpy(m_szURL, lpszURL, _MAX_PATH);
+ } // if
+
+ return BTNST_OK;
+} // End of SetURL
+
+// This function associates a menu to the button.
+// The menu will be displayed clicking the button.
+//
+// Parameters:
+// [IN] nMenu
+// ID number of the menu resource.
+// Pass NULL to remove any menu from the button.
+// [IN] hParentWnd
+// Handle to the window that owns the menu.
+// This window receives all messages from the menu.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDRESOURCE
+// Failed loading the specified resource.
+//
+#ifndef BTNST_USE_BCMENU
+DWORD CButtonST::SetMenu(UINT nMenu, HWND hParentWnd, BOOL bRepaint)
+{
+ HINSTANCE hInstResource = NULL;
+
+ // Destroy any previous menu
+ if (m_hMenu)
+ {
+ VERIFY( ::DestroyMenu(m_hMenu) );
+ m_hMenu = NULL;
+ m_hParentWndMenu = NULL;
+ m_bMenuDisplayed = FALSE;
+ } // if
+
+ // Load menu
+ if (nMenu)
+ {
+ // Find correct resource handle
+ hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nMenu), RT_MENU);
+ // Load menu resource
+ m_hMenu = ::LoadMenu(hInstResource, MAKEINTRESOURCE(nMenu));
+ m_hParentWndMenu = hParentWnd;
+ // If something wrong
+ if (m_hMenu == NULL) return BTNST_INVALIDRESOURCE;
+ } // if
+
+ // Repaint the button
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of SetMenu
+#endif
+
+// This function associates a menu to the button.
+// The menu will be displayed clicking the button.
+// The menu will be handled by the BCMenu class.
+//
+// Parameters:
+// [IN] nMenu
+// ID number of the menu resource.
+// Pass NULL to remove any menu from the button.
+// [IN] hParentWnd
+// Handle to the window that owns the menu.
+// This window receives all messages from the menu.
+// [IN] bWinXPStyle
+// If TRUE the menu will be displayed using the new Windows XP style.
+// If FALSE the menu will be displayed using the standard style.
+// [IN] nToolbarID
+// Resource ID of the toolbar to be associated to the menu.
+// [IN] sizeToolbarIcon
+// A CSize object indicating the size (in pixels) of each icon into the toolbar.
+// All icons into the toolbar must have the same size.
+// [IN] crToolbarBk
+// A COLORREF value indicating the color to use as background for the icons into the toolbar.
+// This color will be used as the "transparent" color.
+// [IN] bRepaint
+// If TRUE the control will be repainted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+// BTNST_INVALIDRESOURCE
+// Failed loading the specified resource.
+//
+#ifdef BTNST_USE_BCMENU
+DWORD CButtonST::SetMenu(UINT nMenu, HWND hParentWnd, BOOL bWinXPStyle, UINT nToolbarID, CSize sizeToolbarIcon, COLORREF crToolbarBk, BOOL bRepaint)
+{
+ BOOL bRetValue = FALSE;
+
+ // Destroy any previous menu
+ if (m_menuPopup.m_hMenu)
+ {
+ VERIFY( m_menuPopup.DestroyMenu() );
+ m_hParentWndMenu = NULL;
+ m_bMenuDisplayed = FALSE;
+ } // if
+
+ // Load menu
+ if (nMenu)
+ {
+ m_menuPopup.SetMenuDrawMode(bWinXPStyle);
+ // Load menu
+ bRetValue = m_menuPopup.LoadMenu(nMenu);
+ // If something wrong
+ if (bRetValue == FALSE) return BTNST_INVALIDRESOURCE;
+
+ // Load toolbar
+ if (nToolbarID)
+ {
+ m_menuPopup.SetBitmapBackground(crToolbarBk);
+ m_menuPopup.SetIconSize(sizeToolbarIcon.cx, sizeToolbarIcon.cy);
+
+ bRetValue = m_menuPopup.LoadToolbar(nToolbarID);
+ // If something wrong
+ if (bRetValue == FALSE)
+ {
+ VERIFY( m_menuPopup.DestroyMenu() );
+ return BTNST_INVALIDRESOURCE;
+ } // if
+ } // if
+
+ m_hParentWndMenu = hParentWnd;
+ } // if
+
+ // Repaint the button
+ if (bRepaint) Invalidate();
+
+ return BTNST_OK;
+} // End of SetMenu
+#endif
+
+// This function sets the callback message that will be sent to the
+// specified window just before the menu associated to the button is displayed.
+//
+// Parameters:
+// [IN] hWnd
+// Handle of the window that will receive the callback message.
+// Pass NULL to remove any previously specified callback message.
+// [IN] nMessage
+// Callback message to send to window.
+// [IN] lParam
+// A 32 bits user specified value that will be passed to the callback function.
+//
+// Remarks:
+// the callback function must be in the form:
+// LRESULT On_MenuCallback(WPARAM wParam, LPARAM lParam)
+// Where:
+// [IN] wParam
+// If support for BCMenu is enabled: a pointer to BCMenu
+// else a HMENU handle to the menu that is being to be displayed.
+// [IN] lParam
+// The 32 bits user specified value.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::SetMenuCallback(HWND hWnd, UINT nMessage, LPARAM lParam)
+{
+ m_csCallbacks.hWnd = hWnd;
+ m_csCallbacks.nMessage = nMessage;
+ m_csCallbacks.lParam = lParam;
+
+ return BTNST_OK;
+} // End of SetMenuCallback
+
+// This function resizes the button to the same size of the image.
+// To get good results both the IN and OUT images should have the same size.
+//
+void CButtonST::SizeToContent()
+{
+ if (m_csIcons[0].hIcon)
+ {
+ m_ptImageOrg.x = 0;
+ m_ptImageOrg.y = 0;
+ SetWindowPos( NULL, -1, -1, m_csIcons[0].dwWidth, m_csIcons[0].dwHeight,
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
+ } // if
+ else
+ if (m_csBitmaps[0].hBitmap)
+ {
+ m_ptImageOrg.x = 0;
+ m_ptImageOrg.y = 0;
+ SetWindowPos( NULL, -1, -1, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight,
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
+ } // if
+} // End of SizeToContent
+
+// This function sets the sound that must be played on particular button states.
+//
+// Parameters:
+// [IN] lpszSound
+// A string that specifies the sound to play.
+// If hMod is NULL this string is interpreted as a filename, else it
+// is interpreted as a resource identifier.
+// Pass NULL to remove any previously specified sound.
+// [IN] hMod
+// Handle to the executable file that contains the resource to be loaded.
+// This parameter must be NULL unless lpszSound specifies a resource identifier.
+// [IN] bPlayOnClick
+// TRUE if the sound must be played when the button is clicked.
+// FALSE if the sound must be played when the mouse is moved over the button.
+// [IN] bPlayAsync
+// TRUE if the sound must be played asynchronously.
+// FALSE if the sound must be played synchronously. The application takes control
+// when the sound is completely played.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+#ifdef BTNST_USE_SOUND
+DWORD CButtonST::SetSound(LPCTSTR lpszSound, HMODULE hMod, BOOL bPlayOnClick, BOOL bPlayAsync)
+{
+ BYTE byIndex = bPlayOnClick ? 1 : 0;
+
+ // Store new sound
+ if (lpszSound)
+ {
+ if (hMod) // From resource identifier ?
+ {
+ m_csSounds[byIndex].lpszSound = lpszSound;
+ } // if
+ else
+ {
+ _tcscpy(m_csSounds[byIndex].szSound, lpszSound);
+ m_csSounds[byIndex].lpszSound = m_csSounds[byIndex].szSound;
+ } // else
+
+ m_csSounds[byIndex].hMod = hMod;
+ m_csSounds[byIndex].dwFlags = SND_NODEFAULT | SND_NOWAIT;
+ m_csSounds[byIndex].dwFlags |= hMod ? SND_RESOURCE : SND_FILENAME;
+ m_csSounds[byIndex].dwFlags |= bPlayAsync ? SND_ASYNC : SND_SYNC;
+ } // if
+ else
+ {
+ // Or remove any existing
+ ::ZeroMemory(&m_csSounds[byIndex], sizeof(STRUCT_SOUND));
+ } // else
+
+ return BTNST_OK;
+} // End of SetSound
+#endif
+
+// This function is called every time the button background needs to be painted.
+// If the button is in transparent mode this function will NOT be called.
+// This is a virtual function that can be rewritten in CButtonST-derived classes
+// to produce a whole range of buttons not available by default.
+//
+// Parameters:
+// [IN] pDC
+// Pointer to a CDC object that indicates the device context.
+// [IN] pRect
+// Pointer to a CRect object that indicates the bounds of the
+// area to be painted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::OnDrawBackground(CDC* pDC, LPCRECT pRect)
+{
+ COLORREF crColor;
+
+ if (m_bMouseOnButton || m_bIsPressed)
+ crColor = m_crColors[BTNST_COLOR_BK_IN];
+ else
+ {
+ if (m_bIsFocused)
+ crColor = m_crColors[BTNST_COLOR_BK_FOCUS];
+ else
+ crColor = m_crColors[BTNST_COLOR_BK_OUT];
+ } // else
+
+ CBrush brBackground(crColor);
+
+ pDC->FillRect(pRect, &brBackground);
+
+ return BTNST_OK;
+} // End of OnDrawBackground
+
+// This function is called every time the button border needs to be painted.
+// If the button is in standard (not flat) mode this function will NOT be called.
+// This is a virtual function that can be rewritten in CButtonST-derived classes
+// to produce a whole range of buttons not available by default.
+//
+// Parameters:
+// [IN] pDC
+// Pointer to a CDC object that indicates the device context.
+// [IN] pRect
+// Pointer to a CRect object that indicates the bounds of the
+// area to be painted.
+//
+// Return value:
+// BTNST_OK
+// Function executed successfully.
+//
+DWORD CButtonST::OnDrawBorder(CDC* pDC, LPCRECT pRect)
+{
+ if (m_bIsPressed)
+ pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHILIGHT));
+ else
+ pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNHILIGHT), ::GetSysColor(COLOR_BTNSHADOW));
+
+ return BTNST_OK;
+} // End of OnDrawBorder
+
+#undef BS_TYPEMASK
diff --git a/BtnST.h b/BtnST.h
new file mode 100644
index 00000000..1dcd8a03
--- /dev/null
+++ b/BtnST.h
@@ -0,0 +1,303 @@
+//
+// Class: CButtonST
+//
+// Compiler: Visual C++
+// Tested on: Visual C++ 5.0
+// Visual C++ 6.0
+//
+// Version: See GetVersionC() or GetVersionI()
+//
+// Created: xx/xxxx/1998
+// Updated: 22/July/2002
+//
+// Author: Davide Calabro' davide_calabro@yahoo.com
+// http://www.softechsoftware.it
+//
+// Note: Code for the PreSubclassWindow and OnSetStyle functions
+// has been taken from the COddButton class
+// published by Paolo Messina and Jerzy Kaczorowski
+//
+// Disclaimer
+// ----------
+// THIS SOFTWARE AND THE ACCOMPANYING FILES ARE DISTRIBUTED "AS IS" AND WITHOUT
+// ANY WARRANTIES WHETHER EXPRESSED OR IMPLIED. NO REPONSIBILITIES FOR POSSIBLE
+// DAMAGES OR EVEN FUNCTIONALITY CAN BE TAKEN. THE USER MUST ASSUME THE ENTIRE
+// RISK OF USING THIS SOFTWARE.
+//
+// Terms of use
+// ------------
+// THIS SOFTWARE IS FREE FOR PERSONAL USE OR FREEWARE APPLICATIONS.
+// IF YOU USE THIS SOFTWARE IN COMMERCIAL OR SHAREWARE APPLICATIONS YOU
+// ARE GENTLY ASKED TO DONATE 1$ (ONE U.S. DOLLAR) TO THE AUTHOR:
+//
+// Davide Calabro'
+// P.O. Box 65
+// 21019 Somma Lombardo (VA)
+// Italy
+//
+#ifndef _BTNST_H
+#define _BTNST_H
+
+// Uncomment the following 2 lines to enable support for BCMenu class
+//#define BTNST_USE_BCMENU
+//#include "BCMenu.h"
+
+// Uncomment the following line to enable support for sound effects
+//#define BTNST_USE_SOUND
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+// Return values
+#ifndef BTNST_OK
+#define BTNST_OK 0
+#endif
+#ifndef BTNST_INVALIDRESOURCE
+#define BTNST_INVALIDRESOURCE 1
+#endif
+#ifndef BTNST_FAILEDMASK
+#define BTNST_FAILEDMASK 2
+#endif
+#ifndef BTNST_INVALIDINDEX
+#define BTNST_INVALIDINDEX 3
+#endif
+#ifndef BTNST_INVALIDALIGN
+#define BTNST_INVALIDALIGN 4
+#endif
+#ifndef BTNST_BADPARAM
+#define BTNST_BADPARAM 5
+#endif
+#ifndef BTNST_INVALIDPRESSEDSTYLE
+#define BTNST_INVALIDPRESSEDSTYLE 6
+#endif
+
+// Dummy identifier for grayscale icon
+#ifndef BTNST_AUTO_GRAY
+#define BTNST_AUTO_GRAY (HICON)(0xffffffff - 1L)
+#endif
+
+class CButtonST : public CButton
+{
+public:
+ CButtonST();
+ ~CButtonST();
+
+ enum { ST_ALIGN_HORIZ = 0, // Icon/bitmap on the left, text on the right
+ ST_ALIGN_VERT, // Icon/bitmap on the top, text on the bottom
+ ST_ALIGN_HORIZ_RIGHT, // Icon/bitmap on the right, text on the left
+ ST_ALIGN_OVERLAP // Icon/bitmap on the same space as text
+ };
+
+ enum { BTNST_COLOR_BK_IN = 0, // Background color when mouse is INside
+ BTNST_COLOR_FG_IN, // Text color when mouse is INside
+ BTNST_COLOR_BK_OUT, // Background color when mouse is OUTside
+ BTNST_COLOR_FG_OUT, // Text color when mouse is OUTside
+ BTNST_COLOR_BK_FOCUS, // Background color when the button is focused
+ BTNST_COLOR_FG_FOCUS, // Text color when the button is focused
+ BTNST_MAX_COLORS
+ };
+
+ enum { BTNST_PRESSED_LEFTRIGHT = 0, // Pressed style from left to right (as usual)
+ BTNST_PRESSED_TOPBOTTOM // Pressed style from top to bottom
+ };
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CButtonST)
+ public:
+ virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+ protected:
+ virtual void PreSubclassWindow();
+ //}}AFX_VIRTUAL
+
+public:
+ DWORD SetDefaultColors(BOOL bRepaint = TRUE);
+ DWORD SetColor(BYTE byColorIndex, COLORREF crColor, BOOL bRepaint = TRUE);
+ DWORD GetColor(BYTE byColorIndex, COLORREF* crpColor);
+ DWORD OffsetColor(BYTE byColorIndex, short shOffset, BOOL bRepaint = TRUE);
+ void SetLeftAlign(bool in) {leftAligned=in;}
+ bool GetLeftAlign() {return leftAligned;}
+ void SetMarked(BOOL in) {marked=in;}
+ DWORD SetCheck(int nCheck, BOOL bRepaint = TRUE);
+ int GetCheck();
+
+ DWORD SetURL(LPCTSTR lpszURL = NULL);
+ void DrawTransparent(BOOL bRepaint = FALSE);
+ DWORD SetBk(CDC* pDC);
+
+ BOOL GetDefault();
+ DWORD SetAlwaysTrack(BOOL bAlwaysTrack = TRUE);
+
+ void SetTooltipText(int nText, BOOL bActivate = TRUE);
+ void SetTooltipText(LPCTSTR lpszText, BOOL bActivate = TRUE);
+ void ActivateTooltip(BOOL bEnable = TRUE);
+ void Repaint() {Invalidate();}
+
+ DWORD SetBtnCursor(int nCursorId = NULL, BOOL bRepaint = TRUE);
+
+ DWORD SetFlat(BOOL bFlat = TRUE, BOOL bRepaint = TRUE);
+ DWORD SetAlign(BYTE byAlign, BOOL bRepaint = TRUE);
+ DWORD SetPressedStyle(BYTE byStyle, BOOL bRepaint = TRUE);
+
+ DWORD DrawBorder(BOOL bDrawBorder = TRUE, BOOL bRepaint = TRUE);
+ DWORD DrawFlatFocus(BOOL bDrawFlatFocus, BOOL bRepaint = TRUE);
+
+ DWORD SetIcon(HICON hIconIn, HICON hIconOut = NULL);
+ DWORD SetIcon(LPCTSTR hIconIn, LPCTSTR hIconOut = NULL);
+
+ DWORD SetBitmaps(int nBitmapIn, COLORREF crTransColorIn, int nBitmapOut = NULL, COLORREF crTransColorOut = 0);
+ DWORD SetBitmaps(HBITMAP hBitmapIn, COLORREF crTransColorIn, HBITMAP hBitmapOut = NULL, COLORREF crTransColorOut = 0);
+
+ void SizeToContent();
+
+#ifdef BTNST_USE_BCMENU
+ DWORD SetMenu(UINT nMenu, HWND hParentWnd, BOOL bWinXPStyle = TRUE, UINT nToolbarID = NULL, CSize sizeToolbarIcon = CSize(16, 16), COLORREF crToolbarBk = RGB(255, 0, 255), BOOL bRepaint = TRUE);
+#else
+ DWORD SetMenu(UINT nMenu, HWND hParentWnd, BOOL bRepaint = TRUE);
+#endif
+ DWORD SetMenuCallback(HWND hWnd, UINT nMessage, LPARAM lParam = 0);
+
+#ifdef BTNST_USE_SOUND
+ DWORD SetSound(LPCTSTR lpszSound, HMODULE hMod = NULL, BOOL bPlayOnClick = FALSE, BOOL bPlayAsync = TRUE);
+#endif
+
+ static short GetVersionI() {return 37;}
+ static LPCTSTR GetVersionC() {return (LPCTSTR)_T("3.7");}
+
+ BOOL m_bShowDisabledBitmap;
+ POINT m_ptImageOrg;
+ POINT m_ptPressedOffset;
+
+protected:
+ //{{AFX_MSG(CButtonST)
+ afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
+ afx_msg void OnKillFocus(CWnd* pNewWnd);
+ afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+ afx_msg void OnSysColorChange();
+ afx_msg BOOL OnClicked();
+ afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
+ afx_msg void OnEnable(BOOL bEnable);
+ afx_msg void OnCancelMode();
+ afx_msg UINT OnGetDlgCode();
+ //}}AFX_MSG
+
+#ifdef BTNST_USE_BCMENU
+ afx_msg LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu);
+ afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
+#endif
+
+ afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
+ HICON CreateGrayscaleIcon(HICON hIcon);
+ virtual DWORD OnDrawBackground(CDC* pDC, LPCRECT pRect);
+ virtual DWORD OnDrawBorder(CDC* pDC, LPCRECT pRect);
+
+ BOOL m_bIsFlat; // Is a flat button?
+ BOOL m_bMouseOnButton; // Is mouse over the button?
+ BOOL m_bDrawTransparent; // Draw transparent?
+ BOOL m_bIsPressed; // Is button pressed?
+ BOOL m_bIsFocused; // Is button focused?
+ BOOL m_bIsDisabled; // Is button disabled?
+ BOOL m_bIsDefault; // Is default button?
+ BOOL m_bIsCheckBox; // Is the button a checkbox?
+ BYTE m_byAlign; // Align mode
+ BOOL m_bDrawBorder; // Draw border?
+ BOOL m_bDrawFlatFocus; // Draw focus rectangle for flat button?
+ COLORREF m_crColors[BTNST_MAX_COLORS]; // Colors to be used
+ HWND m_hParentWndMenu; // Handle to window for menu selection
+ BOOL m_bMenuDisplayed; // Is menu displayed ?
+
+#ifdef BTNST_USE_BCMENU
+ BCMenu m_menuPopup; // BCMenu class instance
+#else
+ HMENU m_hMenu; // Handle to associated menu
+#endif
+
+private:
+ LRESULT OnSetCheck(WPARAM wParam, LPARAM lParam);
+ LRESULT OnGetCheck(WPARAM wParam, LPARAM lParam);
+ LRESULT OnSetStyle(WPARAM wParam, LPARAM lParam);
+ LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
+
+ void FreeResources(BOOL bCheckForNULL = TRUE);
+ void PrepareImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage);
+ HBITMAP CreateBitmapMask(HBITMAP hSourceBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTransColor);
+ virtual void DrawTheIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+ virtual void DrawTheBitmap(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+ virtual void DrawTheText(CDC* pDC, LPCTSTR lpszText, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+ void PaintBk(CDC* pDC);
+
+ void InitToolTip();
+
+ HCURSOR m_hCursor; // Handle to cursor
+ CToolTipCtrl m_ToolTip; // Tooltip
+
+ CDC m_dcBk;
+ CBitmap m_bmpBk;
+ CBitmap* m_pbmpOldBk;
+
+ void CancelHover();
+ BOOL m_bAlwaysTrack; // Always hilight button?
+ int m_nCheck; // Current value for checkbox
+ bool leftAligned;
+ BOOL marked;
+ UINT m_nTypeStyle; // Button style
+
+ TCHAR m_szURL[_MAX_PATH]; // URL to open when clicked
+
+#pragma pack(1)
+ typedef struct _STRUCT_ICONS
+ {
+ HICON hIcon; // Handle to icon
+ DWORD dwWidth; // Width of icon
+ DWORD dwHeight; // Height of icon
+ } STRUCT_ICONS;
+#pragma pack()
+
+#pragma pack(1)
+ typedef struct _STRUCT_BITMAPS
+ {
+ HBITMAP hBitmap; // Handle to bitmap
+ DWORD dwWidth; // Width of bitmap
+ DWORD dwHeight; // Height of bitmap
+ HBITMAP hMask; // Handle to mask bitmap
+ COLORREF crTransparent; // Transparent color
+ } STRUCT_BITMAPS;
+#pragma pack()
+
+#pragma pack(1)
+ typedef struct _STRUCT_CALLBACK
+ {
+ HWND hWnd; // Handle to window
+ UINT nMessage; // Message identifier
+ WPARAM wParam;
+ LPARAM lParam;
+ } STRUCT_CALLBACK;
+#pragma pack()
+
+ STRUCT_ICONS m_csIcons[2];
+ STRUCT_BITMAPS m_csBitmaps[2];
+
+ STRUCT_CALLBACK m_csCallbacks;
+
+#ifdef BTNST_USE_SOUND
+#pragma pack(1)
+ typedef struct _STRUCT_SOUND
+ {
+ TCHAR szSound[_MAX_PATH];
+ LPCTSTR lpszSound;
+ HMODULE hMod;
+ DWORD dwFlags;
+ } STRUCT_SOUND;
+#pragma pack()
+
+ STRUCT_SOUND m_csSounds[2]; // Index 0 = Over 1 = Clicked
+#endif
+
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif
diff --git a/BuddyButton.cpp b/BuddyButton.cpp
new file mode 100644
index 00000000..5d5b04df
--- /dev/null
+++ b/BuddyButton.cpp
@@ -0,0 +1,123 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// Based on idea from http://www.gipsysoft.com/articles/BuddyButton/
+#include "stdafx.h"
+#include "emule.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+static LPCTSTR s_szPropOldWndProc = _T("PropBuddyButtonOldWndProc");
+static LPCTSTR s_szPropBuddyData = _T("PropBuddyButtonData");
+
+struct SBuddyData
+{
+ UINT m_uButtonWidth;
+ HWND m_hwndButton;
+};
+
+static LRESULT CALLBACK BuddyButtonSubClassedProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC pfnOldWndProc = (WNDPROC)GetProp(hWnd, s_szPropOldWndProc);
+ ASSERT( pfnOldWndProc != NULL );
+
+ SBuddyData *pBuddyData = (SBuddyData *)GetProp(hWnd, s_szPropBuddyData);
+ ASSERT( pBuddyData != NULL );
+
+ switch (uMessage)
+ {
+ case WM_NCDESTROY:
+ SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pfnOldWndProc);
+ VERIFY( RemoveProp(hWnd, s_szPropOldWndProc) != NULL );
+ VERIFY( RemoveProp(hWnd, s_szPropBuddyData) != NULL );
+ delete pBuddyData;
+ break;
+
+ case WM_NCHITTEST: {
+ LRESULT lResult = CallWindowProc(pfnOldWndProc, hWnd, uMessage, wParam, lParam);
+ if (lResult == HTNOWHERE)
+ lResult = HTTRANSPARENT;
+ return lResult;
+ }
+
+ case WM_NCCALCSIZE: {
+ LRESULT lResult = CallWindowProc(pfnOldWndProc, hWnd, uMessage, wParam, lParam);
+ LPNCCALCSIZE_PARAMS lpNCCS = (LPNCCALCSIZE_PARAMS)lParam;
+ lpNCCS->rgrc[0].right -= pBuddyData->m_uButtonWidth;
+ return lResult;
+ }
+
+ case WM_SIZE: {
+ CRect rc;
+ GetClientRect(hWnd, rc);
+ rc.left = rc.right;
+ rc.right = rc.left + pBuddyData->m_uButtonWidth;
+ MapWindowPoints(hWnd, GetParent(hWnd), (LPPOINT)&rc, 2);
+ SetWindowPos(pBuddyData->m_hwndButton, NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOZORDER);
+ break;
+ }
+ }
+ return CallWindowProc(pfnOldWndProc, hWnd, uMessage, wParam, lParam);
+}
+
+void AddBuddyButton(HWND hwndEdit, HWND hwndButton)
+{
+ FARPROC lpfnOldWndProc = (FARPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)BuddyButtonSubClassedProc);
+ ASSERT( lpfnOldWndProc != NULL );
+ VERIFY( SetProp(hwndEdit, s_szPropOldWndProc, (HANDLE)lpfnOldWndProc) );
+
+ // Remove the 'flat' style which my have been set by 'InitWindowStyles'
+ DWORD dwButtonStyle = (DWORD)GetWindowLong(hwndButton, GWL_STYLE);
+ if (dwButtonStyle & BS_FLAT)
+ SetWindowLong(hwndButton, GWL_STYLE, dwButtonStyle & ~BS_FLAT);
+
+ CRect rcButton;
+ GetWindowRect(hwndButton, rcButton);
+
+ SBuddyData *pBuddyData = new SBuddyData;
+ pBuddyData->m_uButtonWidth = rcButton.Width();
+ pBuddyData->m_hwndButton = hwndButton;
+ VERIFY( SetProp(hwndEdit, s_szPropBuddyData, (HANDLE)pBuddyData) );
+
+ SetWindowPos(hwndEdit, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+}
+
+bool InitAttachedBrowseButton(HWND hwndButton, HICON &ricoBrowse)
+{
+ // Showing an icon button works for all Windows versions *except* Windows XP w/ active styles
+ if (theApp.IsXPThemeActive())
+ return false;
+ // However, do not stress system resources for non-NT systems.
+ if (afxIsWin95())
+ return false;
+
+ if (ricoBrowse == NULL)
+ {
+ ricoBrowse = theApp.LoadIcon(_T("BrowseFolderSmall"));
+ if (ricoBrowse == NULL)
+ return false;
+ }
+
+ DWORD dwStyle = (DWORD)GetWindowLong(hwndButton, GWL_STYLE);
+ SetWindowLong(hwndButton, GWL_STYLE, dwStyle | BS_ICON);
+ SendMessage(hwndButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)ricoBrowse);
+ return true;
+}
diff --git a/ButtonsTabCtrl.cpp b/ButtonsTabCtrl.cpp
new file mode 100644
index 00000000..0a06a3f1
--- /dev/null
+++ b/ButtonsTabCtrl.cpp
@@ -0,0 +1,205 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ButtonsTabCtrl.h"
+#include "OtherFunctions.h"
+#include "MenuCmds.h"
+#include "UserMsgs.h"
+#include "VisualStylesXP.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+// _WIN32_WINNT >= 0x0501 (XP only)
+#define _WM_THEMECHANGED 0x031A
+#define _ON_WM_THEMECHANGED() \
+ { _WM_THEMECHANGED, 0, 0, 0, AfxSig_l, \
+ (AFX_PMSG)(AFX_PMSGW) \
+ (static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(void) > (_OnThemeChanged)) \
+ },
+
+///////////////////////////////////////////////////////////////////////////////
+// CButtonsTabCtrl
+
+IMPLEMENT_DYNAMIC(CButtonsTabCtrl, CTabCtrl)
+
+BEGIN_MESSAGE_MAP(CButtonsTabCtrl, CTabCtrl)
+ ON_WM_CREATE()
+ _ON_WM_THEMECHANGED()
+END_MESSAGE_MAP()
+
+CButtonsTabCtrl::CButtonsTabCtrl()
+{
+}
+
+CButtonsTabCtrl::~CButtonsTabCtrl()
+{
+}
+
+void CButtonsTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDIS)
+{
+ CRect rect(lpDIS->rcItem);
+ int nTabIndex = lpDIS->itemID;
+ if (nTabIndex < 0)
+ return;
+
+ TCHAR szLabel[256];
+ TC_ITEM tci;
+ tci.mask = TCIF_TEXT;
+ tci.pszText = szLabel;
+ tci.cchTextMax = _countof(szLabel);
+ if (!GetItem(nTabIndex, &tci))
+ return;
+ szLabel[_countof(szLabel) - 1] = _T('\0');
+
+ CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+ if (!pDC)
+ return;
+
+ CRect rcFullItem(lpDIS->rcItem);
+ bool bSelected = (lpDIS->itemState & ODS_SELECTED) != 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // Adding support for XP Styles (Vista Themes) for owner drawn tab controls simply
+ // does *not* work under Vista. Maybe it works under XP (did not try), but that is
+ // meaningless because under XP a owner drawn tab control is already rendered *with*
+ // the proper XP Styles. So, for XP there is no need to care about the theme API at all.
+ //
+ // However, under Vista, a tab control which has the TCS_OWNERDRAWFIXED
+ // style gets additional 3D-borders which are applied by Vista *after* WM_DRAWITEM
+ // was processed. Thus, there is no known workaround available to prevent Vista from
+ // adding those old fashioned 3D-borders. We can render the tab control items within
+ // the WM_DRAWITEM handler in whatever style we want, but Vista will in each case
+ // overwrite the borders of each tab control item with old fashioned 3D-borders...
+ //
+ // To complete this experience, tab controls also do not support NMCUSTOMDRAW. So, the
+ // only known way to customize a tab control is by using TCS_OWNERDRAWFIXED which does
+ // however not work properly under Vista.
+ //
+ // The "solution" which is currently implemented to prevent Vista from drawing those
+ // 3D-borders is by using "ExcludeClipRect" to reduce the drawing area which is used
+ // by Windows after WM_DRAWITEM was processed. This "solution" is very sensitive to
+ // the used rectangles and offsets in general. Incrementing/Decrementing one of the
+ // "rcItem", "rcFullItem", etc. rectangles makes the entire "solution" flawed again
+ // because some borders would become visible again.
+ //
+ HTHEME hTheme = NULL;
+ int iPartId = BP_PUSHBUTTON;
+ int iStateId = PBS_NORMAL;
+ bool bVistaHotTracked = false;
+ bool bVistaThemeActive = theApp.IsVistaThemeActive();
+ if (bVistaThemeActive)
+ {
+ // To determine if the current item is in 'hot tracking' mode, we need to evaluate
+ // the current foreground color - there is no flag which would indicate this state
+ // more safely. This applies only for Vista and for tab controls which have the
+ // TCS_OWNERDRAWFIXED style.
+ bVistaHotTracked = pDC->GetTextColor() == GetSysColor(COLOR_HOTLIGHT);
+
+ hTheme = g_xpStyle.OpenThemeData(m_hWnd, L"BUTTON");
+ if (hTheme)
+ {
+ rcFullItem.InflateRect(2, 2); // get the real tab item rect
+
+ if (bSelected)
+ iStateId = PBS_PRESSED;
+ else
+ iStateId = bVistaHotTracked ? PBS_HOT : PBS_NORMAL;
+
+ // Not very smart, but this is needed (in addition to the DrawThemeBackground and the clipping)
+ // to fix a minor glitch in both of the bottom side corners.
+ CRect rcTopBorder(rcFullItem.left, rcFullItem.top, rcFullItem.right, rcFullItem.top + 2);
+ pDC->FillSolidRect(&rcTopBorder, GetSysColor(COLOR_BTNFACE));
+
+ if (g_xpStyle.IsThemeBackgroundPartiallyTransparent(hTheme, iPartId, iStateId))
+ g_xpStyle.DrawThemeParentBackground(m_hWnd, *pDC, &rcFullItem);
+ g_xpStyle.DrawThemeBackground(hTheme, *pDC, iPartId, iStateId, &rcFullItem, NULL);
+ }
+ }
+
+ // Following background clearing is needed for:
+ // WinXP/Vista (when used without an application theme)
+ // Vista (when used with an application theme but without a theme for the tab control)
+ if ( (!g_xpStyle.IsThemeActive() || !g_xpStyle.IsAppThemed())
+ || (hTheme == NULL && bVistaThemeActive) )
+ pDC->FillSolidRect(&lpDIS->rcItem, GetSysColor(COLOR_BTNFACE));
+
+ int iOldBkMode = pDC->SetBkMode(TRANSPARENT);
+
+ COLORREF crOldColor = CLR_NONE;
+ if (bVistaHotTracked)
+ crOldColor = pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
+
+ rect.top += 2;
+ // Vista: Tab control has troubles with determining the width of a tab if the
+ // label contains one '&' character. To get around this, we use the old code which
+ // replaces one '&' character with two '&' characters and we do not specify DT_NOPREFIX
+ // here when drawing the text.
+ //
+ // Vista: "DrawThemeText" can not be used in case we need a certain foreground color. Thus we always us
+ // "DrawText" to always get the same font and metrics (just for safety).
+ pDC->DrawText(szLabel, rect, DT_SINGLELINE | DT_TOP | DT_CENTER /*| DT_NOPREFIX*/);
+
+ if (crOldColor != CLR_NONE)
+ pDC->SetTextColor(crOldColor);
+ pDC->SetBkMode(iOldBkMode);
+
+ if (hTheme)
+ {
+ pDC->ExcludeClipRect(&rcFullItem);
+ g_xpStyle.CloseThemeData(hTheme);
+ }
+}
+
+void CButtonsTabCtrl::PreSubclassWindow()
+{
+ CTabCtrl::PreSubclassWindow();
+ InternalInit();
+}
+
+int CButtonsTabCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ if (CTabCtrl::OnCreate(lpCreateStruct) == -1)
+ return -1;
+ InternalInit();
+ return 0;
+}
+
+void CButtonsTabCtrl::InternalInit()
+{
+ if (theApp.IsVistaThemeActive()) {
+ ModifyStyle(0, TCS_OWNERDRAWFIXED);
+ ModifyStyle(0, TCS_HOTTRACK);
+ }
+}
+
+LRESULT CButtonsTabCtrl::_OnThemeChanged()
+{
+ // Owner drawn tab control seems to have troubles with updating itself due to an XP theme change..
+ bool bIsOwnerDrawn = (GetStyle() & TCS_OWNERDRAWFIXED) != 0;
+ if (bIsOwnerDrawn)
+ ModifyStyle(TCS_OWNERDRAWFIXED, 0); // Reset control style to not-owner drawn
+ Default(); // Process original WM_THEMECHANGED message
+ if (bIsOwnerDrawn)
+ ModifyStyle(0, TCS_OWNERDRAWFIXED); // Apply owner drawn style again
+ return 0;
+}
diff --git a/ButtonsTabCtrl.h b/ButtonsTabCtrl.h
new file mode 100644
index 00000000..8421b022
--- /dev/null
+++ b/ButtonsTabCtrl.h
@@ -0,0 +1,20 @@
+#pragma once
+
+class CButtonsTabCtrl : public CTabCtrl
+{
+ DECLARE_DYNAMIC(CButtonsTabCtrl)
+
+public:
+ CButtonsTabCtrl();
+ virtual ~CButtonsTabCtrl();
+
+protected:
+ void InternalInit();
+
+ virtual void PreSubclassWindow();
+ virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ afx_msg LRESULT _OnThemeChanged();
+};
diff --git a/CBase64Coding.cpp b/CBase64Coding.cpp
new file mode 100644
index 00000000..bd73873a
--- /dev/null
+++ b/CBase64Coding.cpp
@@ -0,0 +1,287 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "CBase64Coding.hpp"
+#pragma hdrstop
+
+#define CARRIAGE_RETURN (13)
+#define LINE_FEED (10)
+
+/*
+** Author: Samuel R. Blackburn
+** Internet: wfc@pobox.com
+**
+** You can use it any way you like as long as you don't try to sell it.
+**
+** Any attempt to sell WFC in source code form must have the permission
+** of the original author. You can produce commercial executables with
+** WFC but you can't sell WFC.
+**
+** Copyright, 2000, Samuel R. Blackburn
+**
+** $Workfile: CBase64Coding.cpp $
+** $Revision: 14 $
+** $Modtime: 5/12/00 3:39p $
+** $Reuse Tracing Code: 1 $
+*/
+
+//Modified for use with CAsyncProxySocket, removed tracing code
+
+#if defined( _DEBUG ) && ! defined( WFC_STL )
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+#define END_OF_BASE64_ENCODED_DATA ('=')
+#define BASE64_END_OF_BUFFER (0xFD)
+#define BASE64_IGNORABLE_CHARACTER (0xFE)
+#define BASE64_UNKNOWN_VALUE (0xFF)
+#define BASE64_NUMBER_OF_CHARACTERS_PER_LINE (72)
+
+static inline BYTE __get_character( const BYTE * buffer, const BYTE * decoder_table, int& index, int size_of_buffer )
+{
+ BYTE return_value = 0;
+
+ do
+ {
+ if ( index >= size_of_buffer )
+ {
+ return( BASE64_END_OF_BUFFER );
+ }
+
+ return_value = buffer[ index ];
+ index++;
+ }
+ while( return_value != END_OF_BASE64_ENCODED_DATA &&
+ decoder_table[ return_value ] == BASE64_IGNORABLE_CHARACTER );
+
+ return( return_value );
+}
+
+CBase64Coding::CBase64Coding()
+{
+}
+
+CBase64Coding::~CBase64Coding()
+{
+}
+
+BOOL CBase64Coding::Encode( const char * source, int len, char * destination_string )
+{
+
+ const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ int loop_index = 0;
+ int number_of_bytes_to_encode = len;
+
+ BYTE byte_to_add = 0;
+ BYTE byte_1 = 0;
+ BYTE byte_2 = 0;
+ BYTE byte_3 = 0;
+
+ DWORD number_of_bytes_encoded = (DWORD) ( (double) number_of_bytes_to_encode / (double) 0.75 ) + 1;
+
+ // Now add in the CR/LF pairs, each line is truncated at 72 characters
+
+ // 2000-05-12
+ // Thanks go to Ilia Golubev (ilia@varicom.co.il) for finding a bug here.
+ // I was using number_of_bytes_to_encode rather than number_of_bytes_encoded.
+
+ number_of_bytes_encoded += (DWORD)( ( ( number_of_bytes_encoded / BASE64_NUMBER_OF_CHARACTERS_PER_LINE ) + 1 ) * 2 );
+
+ char * destination = destination_string;
+
+ number_of_bytes_encoded = 0;
+
+ while( loop_index < number_of_bytes_to_encode )
+ {
+ // Output the first byte
+
+ byte_1 = source[ loop_index ];
+ byte_to_add = alphabet[ ( byte_1 >> 2 ) ];
+
+ destination[ number_of_bytes_encoded ] = static_cast< char >( byte_to_add );
+ number_of_bytes_encoded++;
+
+ loop_index++;
+
+ if ( loop_index >= number_of_bytes_to_encode )
+ {
+ // We're at the end of the data to encode
+
+ byte_2 = 0;
+ byte_to_add = alphabet[ ( ( ( byte_1 & 0x03 ) << 4 ) | ( ( byte_2 & 0xF0 ) >> 4 ) ) ];
+
+ destination[ number_of_bytes_encoded ] = byte_to_add;
+ number_of_bytes_encoded++;
+
+ destination[ number_of_bytes_encoded ] = END_OF_BASE64_ENCODED_DATA;
+ number_of_bytes_encoded++;
+
+ destination[ number_of_bytes_encoded ] = END_OF_BASE64_ENCODED_DATA;
+
+ // 1999-09-01
+ // Thanks go to Yurong Lin (ylin@dial.pipex.com) for finding a bug here.
+ // We must NULL terminate the string before letting CString have the buffer back.
+
+ destination[ number_of_bytes_encoded + 1 ] = 0;
+
+ return( TRUE );
+ }
+ else
+ {
+ byte_2 = source[ loop_index ];
+ }
+
+ byte_to_add = alphabet[ ( ( ( byte_1 & 0x03 ) << 4 ) | ( ( byte_2 & 0xF0 ) >> 4 ) ) ];
+
+ destination[ number_of_bytes_encoded ] = byte_to_add;
+ number_of_bytes_encoded++;
+
+ loop_index++;
+
+ if ( loop_index >= number_of_bytes_to_encode )
+ {
+ // We ran out of bytes, we need to add the last half of byte_2 and pad
+ byte_3 = 0;
+
+ byte_to_add = alphabet[ ( ( ( byte_2 & 0x0F ) << 2 ) | ( ( byte_3 & 0xC0 ) >> 6 ) ) ];
+
+ destination[ number_of_bytes_encoded ] = byte_to_add;
+ number_of_bytes_encoded++;
+
+ destination[ number_of_bytes_encoded ] = END_OF_BASE64_ENCODED_DATA;
+
+ // 1999-09-01
+ // Thanks go to Yurong Lin (ylin@dial.pipex.com) for finding a bug here.
+ // We must NULL terminate the string before letting CString have the buffer back.
+
+ destination[ number_of_bytes_encoded + 1 ] = 0;
+
+ return( TRUE );
+ }
+ else
+ {
+ byte_3 = source[ loop_index ];
+ }
+
+ loop_index++;
+
+ byte_to_add = alphabet[ ( ( ( byte_2 & 0x0F ) << 2 ) | ( ( byte_3 & 0xC0 ) >> 6 ) ) ];
+
+ destination[ number_of_bytes_encoded ] = byte_to_add;
+ number_of_bytes_encoded++;
+
+ byte_to_add = alphabet[ ( byte_3 & 0x3F ) ];
+
+ destination[ number_of_bytes_encoded ] = byte_to_add;
+ number_of_bytes_encoded++;
+
+ if ( ( number_of_bytes_encoded % BASE64_NUMBER_OF_CHARACTERS_PER_LINE ) == 0 )
+ {
+ destination[ number_of_bytes_encoded ] = CARRIAGE_RETURN;
+ number_of_bytes_encoded++;
+
+ destination[ number_of_bytes_encoded ] = LINE_FEED;
+ number_of_bytes_encoded++;
+ }
+ }
+
+ destination[ number_of_bytes_encoded ] = END_OF_BASE64_ENCODED_DATA;
+
+ // 1999-09-01
+ // Thanks go to Yurong Lin (ylin@dial.pipex.com) for finding a bug here.
+ // We must NULL terminate the string before letting CString have the buffer back.
+
+ destination[ number_of_bytes_encoded + 1 ] = 0;
+
+ return( TRUE );
+}
+
+// End of source
+
+#if 0
+
+
+
+WFC - CBase64Coding
+
+
+
+
+
+
+CBase64Coding
+
+$Revision: 14 $
+
+Description
+
+This class gives you the ability to encode/decode data using base64.
+
+Constructors
+
+
+
+CBase64Coding()
-
+Constructs this object.
+
+
+
+Methods
+
+
+
+BOOL Decode( const CByteArray& source, CByteArray& destination )
+BOOL Decode( const CString& source, CByteArray& destination )
-
+This method takes base64 encoded text and produces the bytes. It decodes
+the base64 encoding.
+
+
BOOL Encode( const CByteArray& source, CByteArray& destination )
+BOOL Encode( const CByteArray& source, CString& destination )
-
+This method takes bytes and turns them into base64 text.
+
+
+
+Example
+#include <wfc.h>
+
+int _tmain( int number_of_command_line_arguments, LPCTSTR command_line_arguments[] )
+{
+ WFCTRACEINIT( TEXT( "_tmain()" ) );
+
+ CByteArray bytes;
+
+ get_file_contents( command_line_arguments[ 0 ], bytes );
+
+ CBase64Coding encoder;
+
+ CString encoded_data;
+
+ if ( encoder.Encode( bytes, encoded_data ) != FALSE )
+ {
+ _tprintf( TEXT( "%s\n", (LPCTSTR) encoded_data );
+ }
+}
+
Copyright, 2000, Samuel R. Blackburn
+$Workfile: CBase64Coding.cpp $
+$Modtime: 5/12/00 3:39p $
+
+
+
+#endif
diff --git a/CBase64Coding.hpp b/CBase64Coding.hpp
new file mode 100644
index 00000000..4c4d2ce6
--- /dev/null
+++ b/CBase64Coding.hpp
@@ -0,0 +1,50 @@
+#if ! defined( BASE_64_CODING_CLASS_HEADER )
+
+#pragma once
+
+/*
+** Author: Samuel R. Blackburn
+** Internet: wfc@pobox.com
+**
+** You can use it any way you like as long as you don't try to sell it.
+**
+** Any attempt to sell WFC in source code form must have the permission
+** of the original author. You can produce commercial executables with
+** WFC but you can't sell WFC.
+**
+** Copyright, 2000, Samuel R. Blackburn
+**
+** $Workfile: CBase64Coding.hpp $
+** $Revision: 3 $
+** $Modtime: 1/04/00 4:39a $
+*/
+
+#define BASE_64_CODING_CLASS_HEADER
+
+class CBase64Coding
+{
+ private:
+
+ // Don't allow canonical behavior (i.e. don't allow this class
+ // to be passed by value)
+
+ CBase64Coding( const CBase64Coding& ) {};
+ CBase64Coding& operator=( const CBase64Coding& ) { return( *this ); };
+
+ public:
+
+ // Construction
+
+ CBase64Coding();
+
+ /*
+ ** Destructor should be virtual according to MSJ article in Sept 1992
+ ** "Do More with Less Code:..."
+ */
+
+ virtual ~CBase64Coding();
+
+ virtual BOOL Encode( const char * source, int len, char * destination );
+};
+
+#endif // BASE_64_CODING_CLASS_HEADER
diff --git a/CaptchaGenerator.cpp b/CaptchaGenerator.cpp
new file mode 100644
index 00000000..09cb06cc
--- /dev/null
+++ b/CaptchaGenerator.cpp
@@ -0,0 +1,104 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#include "StdAfx.h"
+#include "CaptchaGenerator.h"
+#include "CxImage/xImage.h"
+#include "OtherFunctions.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+#define LETTERSIZE 32
+#define CROWDEDSIZE 18
+
+// fairly simply captcha generator, might be improved is spammers think its really worth it solving captchas on eMule
+
+static const char schCaptchaContent[34] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'
+, 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+CCaptchaGenerator::CCaptchaGenerator(uint32 nLetterCount)
+{
+ m_pimgCaptcha = NULL;
+ ReGenerateCaptcha(nLetterCount);
+}
+
+CCaptchaGenerator::~CCaptchaGenerator(void)
+{
+ Clear();
+}
+
+void CCaptchaGenerator::ReGenerateCaptcha(uint32 nLetterCount)
+{
+ Clear();
+ CxImage* pimgResult = new CxImage(nLetterCount > 1 ? (LETTERSIZE + (nLetterCount-1)*CROWDEDSIZE) : LETTERSIZE, 32, 1, CXIMAGE_FORMAT_BMP);
+ pimgResult->SetPaletteColor(0, 255, 255, 255);
+ pimgResult->SetPaletteColor(1, 0, 0, 0, 0);
+ pimgResult->Clear();
+ CxImage imgBlank(LETTERSIZE, LETTERSIZE, 1, CXIMAGE_FORMAT_BMP);
+ imgBlank.SetPaletteColor(0, 255, 255, 255);
+ imgBlank.SetPaletteColor(1, 0, 0, 0, 0);
+ imgBlank.Clear();
+ for (uint32 i = 0; i < nLetterCount; i++) {
+ CxImage imgLetter(imgBlank);
+
+ CString strLetter(schCaptchaContent[GetRandomUInt16() % ARRSIZE(schCaptchaContent)]);
+ m_strCaptchaText += strLetter;
+
+ uint16 nRandomSize = GetRandomUInt16() % 10;
+ uint16 nRandomOffset = 3 + GetRandomUInt16() % 11;
+ imgLetter.DrawString(NULL, nRandomOffset, 32, strLetter, imgLetter.RGBtoRGBQUAD(RGB(0, 0, 0)), _T("Arial"), 40 - nRandomSize, 1000);
+ //imgLetter.DrawTextA(NULL, nRandomOffset, 32, strLetter, imgLetter.RGBtoRGBQUAD(RGB(0, 0, 0)), "Arial", 40 - nRandomSize, 1000);
+ float fRotate = (float)(35 - (GetRandomUInt16() % 70));
+ imgLetter.Rotate2(fRotate, NULL, CxImage::IM_BILINEAR, CxImage::OM_BACKGROUND, 0, false, true);
+ uint32 nOffset = i * CROWDEDSIZE;
+ ASSERT( imgLetter.GetHeight() == pimgResult->GetHeight() && pimgResult->GetWidth() >= nOffset + imgLetter.GetWidth() );
+ for (uint32 j = 0; j < imgLetter.GetHeight(); j++)
+ for (uint32 k = 0; k < imgLetter.GetWidth(); k++)
+ if (pimgResult->GetPixelIndex(nOffset + k, j) != 1)
+ pimgResult->SetPixelIndex(nOffset + k, j, imgLetter.GetPixelIndex(k, j));
+ }
+ pimgResult->Jitter(1);
+ //pimgResult->Save("D:\\CaptchaTest.bmp", CXIMAGE_FORMAT_BMP);
+ m_pimgCaptcha = pimgResult;
+}
+
+void CCaptchaGenerator::Clear(){
+ delete m_pimgCaptcha;
+ m_pimgCaptcha = NULL;
+ m_strCaptchaText = _T("");
+}
+
+bool CCaptchaGenerator::WriteCaptchaImage(CFileDataIO& file)
+{
+ if (m_pimgCaptcha == NULL)
+ return false;
+ BYTE* pbyBuffer = NULL;
+ long ulSize = 0;
+ if (m_pimgCaptcha->Encode(pbyBuffer, ulSize, CXIMAGE_FORMAT_BMP)){
+ file.Write(pbyBuffer, ulSize);
+ ASSERT( ulSize > 100 && ulSize < 1000 );
+ free(pbyBuffer);
+ return true;
+ }
+ else
+ return false;
+}
\ No newline at end of file
diff --git a/CaptchaGenerator.h b/CaptchaGenerator.h
new file mode 100644
index 00000000..10003e2d
--- /dev/null
+++ b/CaptchaGenerator.h
@@ -0,0 +1,38 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#pragma once
+#include "SafeFile.h"
+
+class CxImage;
+
+class CCaptchaGenerator
+{
+public:
+ CCaptchaGenerator(uint32 nLetterCount = 4);
+ ~CCaptchaGenerator(void);
+
+ void ReGenerateCaptcha(uint32 nLetterCount = 4);
+ void Clear();
+ CString GetCaptchaText() const {return m_strCaptchaText;}
+ bool WriteCaptchaImage(CFileDataIO& file);
+
+
+private:
+ CxImage* m_pimgCaptcha;
+ CString m_strCaptchaText;
+};
diff --git a/CatDialog.cpp b/CatDialog.cpp
new file mode 100644
index 00000000..8efed901
--- /dev/null
+++ b/CatDialog.cpp
@@ -0,0 +1,219 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "CustomAutoComplete.h"
+#include "Preferences.h"
+#include "otherfunctions.h"
+#include "SharedFileList.h"
+#include "emuledlg.h"
+#include "TransferDlg.h"
+#include "CatDialog.h"
+#include "UserMsgs.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define REGULAREXPRESSIONS_STRINGS_PROFILE _T("AC_VF_RegExpr.dat")
+
+// CCatDialog dialog
+
+IMPLEMENT_DYNAMIC(CCatDialog, CDialog)
+
+BEGIN_MESSAGE_MAP(CCatDialog, CDialog)
+ ON_BN_CLICKED(IDC_BROWSE, OnBnClickedBrowse)
+ ON_BN_CLICKED(IDOK, OnBnClickedOk)
+ ON_BN_CLICKED(IDC_REB, OnDDBnClicked)
+ ON_MESSAGE(UM_CPN_SELENDOK, OnSelChange) //UM_CPN_SELCHANGE
+END_MESSAGE_MAP()
+
+CCatDialog::CCatDialog(int index)
+ : CDialog(CCatDialog::IDD)
+{
+ m_myCat = thePrefs.GetCategory(index);
+ if (m_myCat == NULL)
+ return;
+ m_pacRegExp=NULL;
+ newcolor = (DWORD)-1;
+}
+
+CCatDialog::~CCatDialog()
+{
+ if (m_pacRegExp){
+ m_pacRegExp->SaveList(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + REGULAREXPRESSIONS_STRINGS_PROFILE);
+ m_pacRegExp->Unbind();
+ m_pacRegExp->Release();
+ }
+}
+
+BOOL CCatDialog::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+ InitWindowStyles(this);
+ Localize();
+ m_ctlColor.SetDefaultColor(GetSysColor(COLOR_BTNTEXT));
+ UpdateData();
+
+ if (!thePrefs.IsExtControlsEnabled()) {
+ GetDlgItem(IDC_REGEXPR)->ShowWindow(SW_HIDE);
+ GetDlgItem(IDC_STATIC_REGEXP)->ShowWindow(SW_HIDE);
+ GetDlgItem(IDC_REGEXP)->ShowWindow(SW_HIDE);
+ GetDlgItem(IDC_REB)->ShowWindow(SW_HIDE);
+ }
+
+ m_pacRegExp = new CCustomAutoComplete();
+ m_pacRegExp->AddRef();
+ if (m_pacRegExp->Bind(::GetDlgItem(m_hWnd, IDC_REGEXP), ACO_UPDOWNKEYDROPSLIST | ACO_AUTOSUGGEST)) {
+ m_pacRegExp->LoadList(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + REGULAREXPRESSIONS_STRINGS_PROFILE);
+ }
+ if (theApp.m_fontSymbol.m_hObject){
+ GetDlgItem(IDC_REB)->SetFont(&theApp.m_fontSymbol);
+ GetDlgItem(IDC_REB)->SetWindowText(_T("6")); // show a down-arrow
+ }
+
+ return TRUE;
+}
+
+void CCatDialog::UpdateData()
+{
+ GetDlgItem(IDC_TITLE)->SetWindowText(m_myCat->strTitle);
+ GetDlgItem(IDC_INCOMING)->SetWindowText(m_myCat->strIncomingPath);
+ GetDlgItem(IDC_COMMENT)->SetWindowText(m_myCat->strComment);
+
+ if (m_myCat->filter==18)
+ SetDlgItemText(IDC_REGEXP,m_myCat->regexp);
+
+ CheckDlgButton(IDC_REGEXPR,m_myCat->ac_regexpeval);
+
+ newcolor = m_myCat->color;
+ m_ctlColor.SetColor(m_myCat->color == -1 ? m_ctlColor.GetDefaultColor() : m_myCat->color);
+
+ GetDlgItem(IDC_AUTOCATEXT)->SetWindowText(m_myCat->autocat);
+
+ m_prio.SetCurSel(m_myCat->prio);
+}
+
+void CCatDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_CATCOLOR, m_ctlColor);
+ DDX_Control(pDX, IDC_PRIOCOMBO, m_prio);
+}
+
+void CCatDialog::Localize()
+{
+ GetDlgItem(IDC_STATIC_TITLE)->SetWindowText(GetResString(IDS_TITLE));
+ GetDlgItem(IDC_STATIC_INCOMING)->SetWindowText(GetResString(IDS_PW_INCOMING) + _T(" ") + GetResString(IDS_SHAREWARNING) );
+ GetDlgItem(IDC_STATIC_COMMENT)->SetWindowText(GetResString(IDS_COMMENT));
+ GetDlgItem(IDCANCEL)->SetWindowText(GetResString(IDS_CANCEL));
+ GetDlgItem(IDC_STATIC_COLOR)->SetWindowText(GetResString(IDS_COLOR));
+ GetDlgItem(IDC_STATIC_PRIO)->SetWindowText(GetResString(IDS_STARTPRIO));
+ GetDlgItem(IDC_STATIC_AUTOCAT)->SetWindowText(GetResString(IDS_AUTOCAT_LABEL));
+ GetDlgItem(IDC_REGEXPR)->SetWindowText(GetResString(IDS_ASREGEXPR));
+ GetDlgItem(IDOK)->SetWindowText(GetResString(IDS_TREEOPTIONS_OK));
+
+ m_ctlColor.CustomText = GetResString(IDS_COL_MORECOLORS);
+ m_ctlColor.DefaultText = GetResString(IDS_DEFAULT);
+
+ SetWindowText(GetResString(IDS_EDITCAT));
+
+ SetDlgItemText(IDC_STATIC_REGEXP,GetResString(IDS_STATIC_REGEXP));
+
+ m_prio.ResetContent();
+ m_prio.AddString(GetResString(IDS_PRIOLOW));
+ m_prio.AddString(GetResString(IDS_PRIONORMAL));
+ m_prio.AddString(GetResString(IDS_PRIOHIGH));
+ m_prio.SetCurSel(m_myCat->prio);
+}
+
+void CCatDialog::OnBnClickedBrowse()
+{
+ TCHAR buffer[MAX_PATH] = {0};
+ GetDlgItemText(IDC_INCOMING, buffer, _countof(buffer));
+ if (SelectDir(GetSafeHwnd(), buffer,GetResString(IDS_SELECT_INCOMINGDIR)))
+ GetDlgItem(IDC_INCOMING)->SetWindowText(buffer);
+}
+
+void CCatDialog::OnBnClickedOk()
+{
+ CString oldpath = m_myCat->strIncomingPath;
+ if (GetDlgItem(IDC_TITLE)->GetWindowTextLength()>0)
+ GetDlgItem(IDC_TITLE)->GetWindowText(m_myCat->strTitle);
+
+ if (GetDlgItem(IDC_INCOMING)->GetWindowTextLength()>2)
+ GetDlgItem(IDC_INCOMING)->GetWindowText(m_myCat->strIncomingPath);
+
+ GetDlgItem(IDC_COMMENT)->GetWindowText(m_myCat->strComment);
+
+ m_myCat->ac_regexpeval= IsDlgButtonChecked(IDC_REGEXPR)>0;
+
+ MakeFoldername(m_myCat->strIncomingPath);
+ if (!thePrefs.IsShareableDirectory(m_myCat->strIncomingPath)){
+ m_myCat->strIncomingPath = thePrefs.GetMuleDirectory(EMULE_INCOMINGDIR);
+ MakeFoldername(m_myCat->strIncomingPath);
+ }
+
+ if (!PathFileExists(m_myCat->strIncomingPath)){
+ if (!::CreateDirectory(m_myCat->strIncomingPath, 0)){
+ AfxMessageBox(GetResString(IDS_ERR_BADFOLDER));
+ m_myCat->strIncomingPath = oldpath;
+ return;
+ }
+ }
+
+ if (m_myCat->strIncomingPath.CompareNoCase(oldpath)!=0)
+ theApp.sharedfiles->Reload();
+
+ m_myCat->color=newcolor;
+ m_myCat->prio=m_prio.GetCurSel();
+ GetDlgItem(IDC_AUTOCATEXT)->GetWindowText(m_myCat->autocat);
+
+ GetDlgItemText(IDC_REGEXP,m_myCat->regexp);
+ if (m_myCat->regexp.GetLength()>0) {
+ if (m_pacRegExp && m_pacRegExp->IsBound()){
+ m_pacRegExp->AddItem(m_myCat->regexp,0);
+ m_myCat->filter=18;
+ }
+ } else if (m_myCat->filter==18) {
+ // deactivate regexp
+ m_myCat->filter=0;
+ }
+
+ theApp.emuledlg->transferwnd->GetDownloadList()->Invalidate();
+
+ OnOK();
+}
+
+LONG CCatDialog::OnSelChange(UINT lParam, LONG /*wParam*/)
+{
+ if (lParam == CLR_DEFAULT)
+ newcolor = (DWORD)-1;
+ else
+ newcolor = m_ctlColor.GetColor();
+ return TRUE;
+}
+
+void CCatDialog::OnDDBnClicked()
+{
+ CWnd* box = GetDlgItem(IDC_REGEXP);
+ box->SetFocus();
+ box->SetWindowText(_T(""));
+ box->SendMessage(WM_KEYDOWN, VK_DOWN, 0x00510001);
+}
diff --git a/CatDialog.h b/CatDialog.h
new file mode 100644
index 00000000..1998a6d0
--- /dev/null
+++ b/CatDialog.h
@@ -0,0 +1,50 @@
+//this file is part of eMule
+// added by quekky
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "ColorButton.h"
+
+struct Category_Struct;
+class CCustomAutoComplete;
+
+class CCatDialog : public CDialog
+{
+ DECLARE_DYNAMIC(CCatDialog)
+public:
+ CCatDialog(int catindex); // standard constructor
+ virtual ~CCatDialog();
+
+ enum { IDD = IDD_CAT };
+
+protected:
+ Category_Struct* m_myCat;
+ DWORD newcolor;
+ CColorButton m_ctlColor;
+ CComboBox m_prio;
+ CCustomAutoComplete* m_pacRegExp;
+
+ void Localize();
+ void UpdateData();
+
+ virtual BOOL OnInitDialog();
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg LONG OnSelChange(UINT lParam, LONG wParam);
+ afx_msg void OnBnClickedBrowse();
+ afx_msg void OnBnClickedOk();
+ afx_msg void OnDDBnClicked();
+};
diff --git a/ChatSelector.cpp b/ChatSelector.cpp
new file mode 100644
index 00000000..3938e2fd
--- /dev/null
+++ b/ChatSelector.cpp
@@ -0,0 +1,678 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ChatSelector.h"
+#include "packets.h"
+#include "HTRichEditCtrl.h"
+#include "emuledlg.h"
+#include "Statistics.h"
+#include "OtherFunctions.h"
+#include "UpDownClient.h"
+#include "Preferences.h"
+#include "TaskbarNotifier.h"
+#include "ListenSocket.h"
+#include "ChatWnd.h"
+#include "SafeFile.h"
+#include "Log.h"
+#include "MenuCmds.h"
+#include "ClientDetailDialog.h"
+#include "FriendList.h"
+#include "ClientList.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define STATUS_MSG_COLOR RGB(0,128,0) // dark green
+#define SENT_TARGET_MSG_COLOR RGB(0,192,0) // bright green
+#define RECV_SOURCE_MSG_COLOR RGB(0,128,255) // bright cyan/blue
+
+#define TIME_STAMP_FORMAT _T("[%H:%M] ")
+
+///////////////////////////////////////////////////////////////////////////////
+// CChatItem
+
+CChatItem::CChatItem()
+{
+ client = NULL;
+ log = NULL;
+ notify = false;
+ history_pos = 0;
+}
+
+CChatItem::~CChatItem()
+{
+ delete log;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CChatSelector
+
+IMPLEMENT_DYNAMIC(CChatSelector, CClosableTabCtrl)
+
+BEGIN_MESSAGE_MAP(CChatSelector, CClosableTabCtrl)
+ ON_WM_SIZE()
+ ON_WM_DESTROY()
+ ON_WM_TIMER()
+ ON_WM_SYSCOLORCHANGE()
+ ON_NOTIFY_REFLECT(TCN_SELCHANGE, OnTcnSelChangeChatSel)
+ ON_WM_CONTEXTMENU()
+END_MESSAGE_MAP()
+
+CChatSelector::CChatSelector()
+{
+ m_lastemptyicon = false;
+ m_blinkstate = false;
+ m_Timer = 0;
+ m_bCloseable = true;
+}
+
+CChatSelector::~CChatSelector()
+{
+}
+
+void CChatSelector::Init(CChatWnd *pParent)
+{
+ m_pParent = pParent;
+
+ ModifyStyle(0, WS_CLIPCHILDREN);
+ SetAllIcons();
+
+ VERIFY( (m_Timer = SetTimer(20, 1500, 0)) != NULL );
+}
+
+void CChatSelector::OnSysColorChange()
+{
+ CClosableTabCtrl::OnSysColorChange();
+ SetAllIcons();
+}
+
+void CChatSelector::SetAllIcons()
+{
+ CImageList iml;
+ iml.Create(16, 16, theApp.m_iDfltImageListColorFlags | ILC_MASK, 0, 1);
+ iml.Add(CTempIconLoader(_T("Chat")));
+ iml.Add(CTempIconLoader(_T("Message")));
+ iml.Add(CTempIconLoader(_T("MessagePending")));
+ SetImageList(&iml);
+ m_imlChat.DeleteImageList();
+ m_imlChat.Attach(iml.Detach());
+ SetPadding(CSize(12, 3));
+}
+
+void CChatSelector::UpdateFonts(CFont* pFont)
+{
+ TCITEM item;
+ item.mask = TCIF_PARAM;
+ int i = 0;
+ while (GetItem(i++, &item)){
+ CChatItem* ci = (CChatItem*)item.lParam;
+ ci->log->SetFont(pFont);
+ }
+}
+
+CChatItem* CChatSelector::StartSession(CUpDownClient* client, bool show)
+{
+ if (show)
+ m_pParent->m_wndMessage.SetFocus();
+ if (GetTabByClient(client) != -1){
+ if (show){
+ SetCurSel(GetTabByClient(client));
+ ShowChat();
+ }
+ return NULL;
+ }
+
+ CChatItem* chatitem = new CChatItem();
+ chatitem->client = client;
+ chatitem->log = new CHTRichEditCtrl;
+
+ CRect rcChat;
+ GetChatSize(rcChat);
+ if (GetItemCount() == 0)
+ rcChat.top += 19; // add the height of the tab which is not yet there
+ // using ES_NOHIDESEL is actually not needed, but it helps to get around a tricky window update problem!
+ // If that style is not specified there are troubles with right clicking into the control for the very first time!?
+ chatitem->log->Create(WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_NOHIDESEL, rcChat, this, (UINT)-1);
+ chatitem->log->ModifyStyleEx(0, WS_EX_STATICEDGE, SWP_FRAMECHANGED);
+ chatitem->log->SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
+ chatitem->log->SetEventMask(chatitem->log->GetEventMask() | ENM_LINK);
+ chatitem->log->SetFont(&theApp.m_fontHyperText);
+ chatitem->log->SetProfileSkinKey(_T("Chat"));
+ chatitem->log->ApplySkin();
+ chatitem->log->EnableSmileys(thePrefs.GetMessageEnableSmileys());
+
+ PARAFORMAT pf = {0};
+ pf.cbSize = sizeof pf;
+ pf.dwMask = PFM_OFFSET;
+ pf.dxOffset = 150;
+ chatitem->log->SetParaFormat(pf);
+
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(chatitem);
+ chatitem->log->AppendKeyWord(GetResString(IDS_CHAT_START) + client->GetUserName() + _T("\n"), STATUS_MSG_COLOR);
+ client->SetChatState(MS_CHATTING);
+
+ CString name;
+ if (client->GetUserName() != NULL)
+ name = client->GetUserName();
+ else
+ name.Format(_T("(%s)"), GetResString(IDS_UNKNOWN));
+ chatitem->log->SetTitle(name);
+
+ TCITEM newitem;
+ newitem.mask = TCIF_PARAM | TCIF_TEXT | TCIF_IMAGE;
+ newitem.lParam = (LPARAM)chatitem;
+ name.Replace(_T("&"), _T("&&"));
+ newitem.pszText = const_cast((LPCTSTR)name);
+ newitem.iImage = 0;
+ int iItemNr = InsertItem(GetItemCount(), &newitem);
+ if (show || IsWindowVisible()){
+ SetCurSel(iItemNr);
+ ShowChat();
+ }
+ return chatitem;
+}
+
+int CChatSelector::GetTabByClient(CUpDownClient* client)
+{
+ for (int i = 0; i < GetItemCount(); i++){
+ TCITEM cur_item;
+ cur_item.mask = TCIF_PARAM;
+ if (GetItem(i, &cur_item) && ((CChatItem*)cur_item.lParam)->client == client)
+ return i;
+ }
+ return -1;
+}
+
+CChatItem* CChatSelector::GetItemByIndex(int index)
+{
+ TCITEM item;
+ item.mask = TCIF_PARAM;
+ if (GetItem(index, &item)==FALSE)
+ return NULL;
+
+ return (CChatItem*)item.lParam;
+}
+
+CChatItem* CChatSelector::GetItemByClient(CUpDownClient* client)
+{
+ for (int i = 0; i < GetItemCount(); i++){
+ TCITEM cur_item;
+ cur_item.mask = TCIF_PARAM;
+ if (GetItem(i, &cur_item) && ((CChatItem*)cur_item.lParam)->client == client)
+ return (CChatItem*)cur_item.lParam;
+ }
+ return NULL;
+}
+
+void CChatSelector::ProcessMessage(CUpDownClient* sender, const CString& message)
+{
+ sender->IncMessagesReceived();
+ CChatItem* ci = GetItemByClient(sender);
+
+ AddLogLine(true, GetResString(IDS_NEWMSG), sender->GetUserName(), ipstr(sender->GetConnectIP()));
+
+ bool isNewChatWindow = false;
+ if (!ci)
+ {
+ if ((UINT)GetItemCount() >= thePrefs.GetMsgSessionsMax())
+ return;
+ ci = StartSession(sender, false);
+ isNewChatWindow = true;
+ }
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(sender->GetUserName(), RECV_SOURCE_MSG_COLOR);
+ ci->log->AppendText(_T(": "));
+ ci->log->AppendText(message + _T("\n"));
+ int iTabItem = GetTabByClient(sender);
+ if (GetCurSel() == iTabItem && GetParent()->IsWindowVisible())
+ {
+ // chat window is already visible
+ ;
+ }
+ else if (GetCurSel() != iTabItem)
+ {
+ // chat window is already visible, but tab is not selected
+ ci->notify = true;
+ }
+ else
+ {
+ ci->notify = true;
+ if (isNewChatWindow || thePrefs.GetNotifierOnEveryChatMsg())
+ theApp.emuledlg->ShowNotifier(GetResString(IDS_TBN_NEWCHATMSG) + _T(" ") + CString(sender->GetUserName()) + _T(":'") + message + _T("'\n"), TBN_CHAT);
+ isNewChatWindow = false;
+ }
+}
+
+void CChatSelector::ShowCaptchaRequest(CUpDownClient* sender, HBITMAP bmpCaptcha)
+{
+ CChatItem* ci = GetItemByClient(sender);
+ if (ci != NULL)
+ {
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(_T("*** ") + GetResString(IDS_CAPTCHAREQUEST), STATUS_MSG_COLOR);
+ ci->log->AddCaptcha(bmpCaptcha);
+ ci->log->AddLine(_T("\n"));
+ }
+}
+
+void CChatSelector::ShowCaptchaResult(CUpDownClient* sender, CString strResult)
+{
+ CChatItem* ci = GetItemByClient(sender);
+ if (ci != NULL)
+ {
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(_T("*** ") + strResult + _T("\n"), STATUS_MSG_COLOR);
+ }
+}
+
+bool CChatSelector::SendMessage(const CString& rstrMessage)
+{
+ CChatItem* ci = GetCurrentChatItem();
+ if (!ci)
+ return false;
+
+ if ((UINT)ci->history.GetCount() == thePrefs.GetMaxChatHistoryLines())
+ ci->history.RemoveAt(0);
+ ci->history.Add(rstrMessage);
+ ci->history_pos = ci->history.GetCount();
+
+ // advance spamfilter stuff
+ ci->client->IncMessagesSent();
+ ci->client->SetSpammer(false);
+ if (ci->client->GetChatState() == MS_CONNECTING)
+ return false;
+
+ if (ci->client->GetChatCaptchaState() == CA_CAPTCHARECV)
+ ci->client->SetChatCaptchaState(CA_SOLUTIONSENT);
+ else if (ci->client->GetChatCaptchaState() == CA_SOLUTIONSENT)
+ ASSERT( false ); // we responsed to a captcha but didn't heard from the client afterwards - hopefully its just lag and this message will get through
+ else
+ ci->client->SetChatCaptchaState(CA_ACCEPTING);
+
+
+
+
+ // there are three cases on connectiing/sending the message:
+ if (ci->client->socket && ci->client->socket->IsConnected())
+ {
+ // 1.) the client is connected already - this is simple, jsut send it
+ ci->client->SendChatMessage(rstrMessage);
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(thePrefs.GetUserNick(), SENT_TARGET_MSG_COLOR);
+ ci->log->AppendText(_T(": "));
+ ci->log->AppendText(rstrMessage + _T("\n"));
+ }
+ else if (ci->client->GetFriend() != NULL)
+ {
+ // We are not connected and this client is a friend - friends have additional ways to connect and additional checks
+ // to make sure they are really friends, let the friend class is handling it
+ ci->strMessagePending = rstrMessage;
+ ci->client->SetChatState(MS_CONNECTING);
+ ci->client->GetFriend()->TryToConnect(this);
+ }
+ else
+ {
+ // this is a normal client, who is not connected right now. just try to connect to the given IP, without any
+ // additional checks or searchings.
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(_T("*** ") + GetResString(IDS_CONNECTING), STATUS_MSG_COLOR);
+ ci->strMessagePending = rstrMessage;
+ ci->client->SetChatState(MS_CONNECTING);
+ ci->client->TryToConnect(true);
+ }
+ return true;
+}
+
+void CChatSelector::ConnectingResult(CUpDownClient* sender, bool success)
+{
+ CChatItem* ci = GetItemByClient(sender);
+ if (!ci)
+ return;
+
+ ci->client->SetChatState(MS_CHATTING);
+ if (!success){
+ if (!ci->strMessagePending.IsEmpty()){
+ ci->log->AppendKeyWord(_T(" ...") + GetResString(IDS_FAILED) + _T("\n"), STATUS_MSG_COLOR);
+ ci->strMessagePending.Empty();
+ }
+ else{
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(GetResString(IDS_CHATDISCONNECTED) + _T("\n"), STATUS_MSG_COLOR);
+ }
+ }
+ else if (!ci->strMessagePending.IsEmpty()){
+ ci->log->AppendKeyWord(_T(" ...") + GetResString(IDS_TREEOPTIONS_OK) + _T("\n"), STATUS_MSG_COLOR);
+ ci->client->SendChatMessage(ci->strMessagePending);
+
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(thePrefs.GetUserNick(), SENT_TARGET_MSG_COLOR);
+ ci->log->AppendText(_T(": "));
+ ci->log->AppendText(ci->strMessagePending + _T("\n"));
+
+ ci->strMessagePending.Empty();
+ }
+ else{
+ if (thePrefs.GetIRCAddTimeStamp())
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(_T("*** Connected\n"), STATUS_MSG_COLOR);
+ }
+}
+
+void CChatSelector::DeleteAllItems()
+{
+ for (int i = 0; i < GetItemCount(); i++){
+ TCITEM cur_item;
+ cur_item.mask = TCIF_PARAM;
+ if (GetItem(i, &cur_item))
+ delete (CChatItem*)cur_item.lParam;
+ }
+}
+
+void CChatSelector::OnTimer(UINT_PTR /*nIDEvent*/)
+{
+ m_blinkstate = !m_blinkstate;
+ bool globalnotify = false;
+ for (int i = 0; i < GetItemCount();i++)
+ {
+ TCITEM cur_item;
+ cur_item.mask = TCIF_PARAM | TCIF_IMAGE;
+ if (!GetItem(i, &cur_item))
+ break;
+
+ cur_item.mask = TCIF_IMAGE;
+ if (((CChatItem*)cur_item.lParam)->notify){
+ cur_item.iImage = (m_blinkstate) ? 1 : 2;
+ SetItem(i, &cur_item);
+ HighlightItem(i, TRUE);
+ globalnotify = true;
+ }
+ else if (cur_item.iImage != 0){
+ cur_item.iImage = 0;
+ SetItem(i, &cur_item);
+ HighlightItem(i, FALSE);
+ }
+ }
+
+ if (globalnotify) {
+ theApp.emuledlg->ShowMessageState(m_blinkstate ? 1 : 2);
+ m_lastemptyicon = false;
+ }
+ else if (!m_lastemptyicon) {
+ theApp.emuledlg->ShowMessageState(0);
+ m_lastemptyicon = true;
+ }
+}
+
+CChatItem* CChatSelector::GetCurrentChatItem()
+{
+ int iCurSel = GetCurSel();
+ if (iCurSel == -1)
+ return NULL;
+
+ TCITEM cur_item;
+ cur_item.mask = TCIF_PARAM;
+ if (!GetItem(iCurSel, &cur_item))
+ return NULL;
+
+ return (CChatItem*)cur_item.lParam;
+}
+
+void CChatSelector::ShowChat()
+{
+ CChatItem* ci = GetCurrentChatItem();
+ if (!ci)
+ return;
+
+ // show current chat window
+ ci->log->ShowWindow(SW_SHOW);
+ m_pParent->m_wndMessage.SetFocus();
+
+ TCITEM item;
+ item.mask = TCIF_IMAGE;
+ item.iImage = 0;
+ SetItem(GetCurSel(), &item);
+ HighlightItem(GetCurSel(), FALSE);
+
+ // hide all other chat windows
+ item.mask = TCIF_PARAM;
+ int i = 0;
+ while (GetItem(i++, &item)){
+ CChatItem* ci2 = (CChatItem*)item.lParam;
+ if (ci2 != ci)
+ ci2->log->ShowWindow(SW_HIDE);
+ }
+
+ ci->notify = false;
+}
+
+void CChatSelector::OnTcnSelChangeChatSel(NMHDR* /*pNMHDR*/, LRESULT* pResult)
+{
+ ShowChat();
+ *pResult = 0;
+}
+
+int CChatSelector::InsertItem(int nItem, TCITEM* pTabCtrlItem)
+{
+ int iResult = CClosableTabCtrl::InsertItem(nItem, pTabCtrlItem);
+ RedrawWindow();
+ return iResult;
+}
+
+BOOL CChatSelector::DeleteItem(int nItem)
+{
+ CClosableTabCtrl::DeleteItem(nItem);
+ RedrawWindow();
+ return TRUE;
+}
+
+void CChatSelector::EndSession(CUpDownClient* client)
+{
+ int iCurSel;
+ if (client)
+ iCurSel = GetTabByClient(client);
+ else
+ iCurSel = GetCurSel();
+ if (iCurSel == -1)
+ return;
+
+ TCITEM item;
+ item.mask = TCIF_PARAM;
+ if (!GetItem(iCurSel, &item) || item.lParam == 0)
+ return;
+ CChatItem* ci = (CChatItem*)item.lParam;
+ ci->client->SetChatState(MS_NONE);
+ ci->client->SetChatCaptchaState(CA_NONE);
+
+ DeleteItem(iCurSel);
+ delete ci;
+
+ int iTabItems = GetItemCount();
+ if (iTabItems > 0){
+ // select next tab
+ if (iCurSel == CB_ERR)
+ iCurSel = 0;
+ else if (iCurSel >= iTabItems)
+ iCurSel = iTabItems - 1;
+ (void)SetCurSel(iCurSel); // returns CB_ERR if error or no prev. selection(!)
+ iCurSel = GetCurSel(); // get the real current selection
+ if (iCurSel == CB_ERR) // if still error
+ iCurSel = SetCurSel(0);
+ ShowChat();
+ }
+}
+
+void CChatSelector::GetChatSize(CRect& rcChat)
+{
+ CRect rcClient;
+ GetClientRect(&rcClient);
+ AdjustRect(FALSE, rcClient);
+ rcChat.left = rcClient.left + 4;
+ rcChat.top = rcClient.top + 4;
+ rcChat.right = rcClient.right - 4;
+ rcChat.bottom = rcClient.bottom - 4;
+}
+
+void CChatSelector::OnSize(UINT nType, int cx, int cy)
+{
+ CClosableTabCtrl::OnSize(nType, cx, cy);
+
+ CRect rcChat;
+ GetChatSize(rcChat);
+
+ TCITEM item;
+ item.mask = TCIF_PARAM;
+ int i = 0;
+ while (GetItem(i++, &item)){
+ CChatItem* ci = (CChatItem*)item.lParam;
+ ci->log->SetWindowPos(NULL, rcChat.left, rcChat.top, rcChat.Width(), rcChat.Height(), SWP_NOZORDER);
+ }
+}
+
+void CChatSelector::AddTimeStamp(CChatItem* ci)
+{
+ ci->log->AppendText(CTime::GetCurrentTime().Format(TIME_STAMP_FORMAT));
+}
+
+void CChatSelector::OnDestroy()
+{
+ if (m_Timer){
+ KillTimer(m_Timer);
+ m_Timer = NULL;
+ }
+ CClosableTabCtrl::OnDestroy();
+}
+
+BOOL CChatSelector::OnCommand(WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam) {
+ case MP_DETAIL:{
+ const CChatItem* ci = GetItemByIndex(m_iContextIndex);
+ if (ci) {
+ CClientDetailDialog dialog(ci->client);
+ dialog.DoModal();
+ }
+ return TRUE;
+ }
+ case MP_ADDFRIEND:{
+ const CChatItem* ci = GetItemByIndex(m_iContextIndex);
+ if (ci) {
+ CFriend* fr = theApp.friendlist->SearchFriend(ci->client->GetUserHash(), 0, 0);
+ if (!fr)
+ theApp.friendlist->AddFriend(ci->client);
+ }
+ return TRUE;
+ }
+ case MP_REMOVEFRIEND:{
+ const CChatItem* ci = GetItemByIndex(m_iContextIndex);
+ if (ci) {
+ CFriend* fr = theApp.friendlist->SearchFriend(ci->client->GetUserHash(), 0, 0);
+ if (fr)
+ theApp.friendlist->RemoveFriend(fr);
+ }
+ return TRUE;
+ }
+ case MP_REMOVE:{
+ const CChatItem* ci = GetItemByIndex(m_iContextIndex);
+ if (ci)
+ EndSession(ci->client);
+ return TRUE;
+ }
+ }
+ return CClosableTabCtrl::OnCommand(wParam, lParam);
+}
+
+void CChatSelector::OnContextMenu(CWnd*, CPoint point)
+{
+ TCHITTESTINFO hti = {0};
+ ::GetCursorPos(&hti.pt);
+ ScreenToClient(&hti.pt);
+
+
+ m_iContextIndex=this->HitTest(&hti);
+ if (m_iContextIndex==-1)
+ return;
+
+ TCITEM item;
+ item.mask = TCIF_PARAM;
+ GetItem(m_iContextIndex, &item);
+
+ const CChatItem* ci = (CChatItem*)item.lParam;
+ if (ci == NULL)
+ return;
+
+ CFriend* pFriend = theApp.friendlist->SearchFriend(ci->client->GetUserHash(), 0, 0);
+
+ CTitleMenu menu;
+ menu.CreatePopupMenu();
+ menu.AddMenuTitle(GetResString(IDS_CLIENT), true);
+
+ menu.AppendMenu(MF_STRING, MP_DETAIL, GetResString(IDS_SHOWDETAILS), _T("CLIENTDETAILS"));
+
+ GetCurrentChatItem();
+ if (pFriend == NULL)
+ menu.AppendMenu(MF_STRING, MP_ADDFRIEND, GetResString(IDS_IRC_ADDTOFRIENDLIST), _T("ADDFRIEND"));
+ else
+ menu.AppendMenu(MF_STRING, MP_REMOVEFRIEND, GetResString(IDS_REMOVEFRIEND), _T("DELETEFRIEND"));
+
+ menu.AppendMenu(MF_STRING, MP_REMOVE, GetResString(IDS_FD_CLOSE));
+
+ m_ptCtxMenu = point;
+ ScreenToClient(&m_ptCtxMenu);
+ menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
+}
+
+void CChatSelector::EnableSmileys(bool bEnable)
+{
+ for (int i = 0; i < GetItemCount(); i++){
+ TCITEM cur_item;
+ cur_item.mask = TCIF_PARAM;
+ if (GetItem(i, &cur_item) && ((CChatItem*)cur_item.lParam)->log)
+ ((CChatItem*)cur_item.lParam)->log->EnableSmileys(bEnable);
+ }
+}
+
+void CChatSelector::ReportConnectionProgress(CUpDownClient* pClient, CString strProgressDesc, bool bNoTimeStamp)
+{
+ CChatItem* ci = GetItemByClient(pClient);
+ if (!ci)
+ return;
+ if (thePrefs.GetIRCAddTimeStamp() && !bNoTimeStamp)
+ AddTimeStamp(ci);
+ ci->log->AppendKeyWord(strProgressDesc, STATUS_MSG_COLOR);
+}
+
+void CChatSelector::ClientObjectChanged(CUpDownClient* pOldClient, CUpDownClient* pNewClient){
+ // the friend has deceided to change the clients objects (because the old doesnt seems to be our friend) during a connectiontry
+ // in order to not close and reopen a new session and loose the prior chat, switch the objects on a existing tab
+ // nothing else changes since the tab is supposed to be still connected to the same friend
+ CChatItem* ci = GetItemByClient(pOldClient);
+ if (!ci)
+ return;
+ ci->client = pNewClient;
+}
\ No newline at end of file
diff --git a/ChatSelector.h b/ChatSelector.h
new file mode 100644
index 00000000..b1ab730f
--- /dev/null
+++ b/ChatSelector.h
@@ -0,0 +1,102 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "ClosableTabCtrl.h"
+#include "Friend.h"
+
+class CChatWnd;
+class CHTRichEditCtrl;
+class CUpDownClient;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CChatItem
+
+class CChatItem
+{
+public:
+ CChatItem();
+ ~CChatItem();
+
+ CUpDownClient* client;
+ CHTRichEditCtrl* log;
+ CString strMessagePending;
+ bool notify;
+ CStringArray history;
+ int history_pos;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CChatSelector
+
+class CChatSelector : public CClosableTabCtrl, CFriendConnectionListener
+{
+ DECLARE_DYNAMIC(CChatSelector)
+
+public:
+ CChatSelector();
+ virtual ~CChatSelector();
+
+ void Init(CChatWnd *pParent);
+ CChatItem* StartSession(CUpDownClient* client, bool show = true);
+ void EndSession(CUpDownClient* client = 0);
+ int GetTabByClient(CUpDownClient* client);
+ CChatItem* GetItemByClient(CUpDownClient* client);
+ CChatItem* GetItemByIndex(int index);
+ void ProcessMessage(CUpDownClient* sender, const CString& message);
+ void ShowCaptchaRequest(CUpDownClient* sender, HBITMAP bmpCaptcha);
+ void ShowCaptchaResult(CUpDownClient* sender, CString strResult);
+ bool SendMessage(const CString& rstrMessage);
+ void DeleteAllItems();
+ void ShowChat();
+ void ConnectingResult(CUpDownClient* sender,bool success);
+ void Send();
+ void UpdateFonts(CFont* pFont);
+ CChatItem* GetCurrentChatItem();
+ BOOL RemoveItem(int nItem) { return DeleteItem(nItem); }
+ void EnableSmileys(bool bEnable);
+ void ReportConnectionProgress(CUpDownClient* pClient, CString strProgressDesc, bool bNoTimeStamp);
+ void ClientObjectChanged(CUpDownClient* pOldClient, CUpDownClient* pNewClient);
+
+protected:
+ CChatWnd *m_pParent;
+ UINT_PTR m_Timer;
+ bool m_blinkstate;
+ bool m_lastemptyicon;
+ CImageList m_imlChat;
+ int m_iContextIndex;
+
+ void AddTimeStamp(CChatItem*);
+ void SetAllIcons();
+ void GetChatSize(CRect& rcChat);
+
+ virtual int InsertItem(int nItem, TCITEM* pTabCtrlItem);
+ virtual BOOL DeleteItem(int nItem);
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnDestroy();
+ afx_msg void OnTimer(UINT_PTR nIDEvent);
+ afx_msg void OnTcnSelChangeChatSel(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnBnClickedCsend();
+ afx_msg void OnBnClickedCclose();
+ afx_msg void OnBnClickedSmiley();
+ afx_msg void OnSysColorChange();
+ afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
+};
diff --git a/ChatWnd.cpp b/ChatWnd.cpp
new file mode 100644
index 00000000..70a8bf16
--- /dev/null
+++ b/ChatWnd.cpp
@@ -0,0 +1,572 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ChatWnd.h"
+#include "HTRichEditCtrl.h"
+#include "FriendList.h"
+#include "emuledlg.h"
+#include "UpDownClient.h"
+#include "OtherFunctions.h"
+#include "HelpIDs.h"
+#include "Opcodes.h"
+#include "friend.h"
+#include "ClientCredits.h"
+#include "IconStatic.h"
+#include "UserMsgs.h"
+#include "SmileySelector.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+#define SPLITTER_HORZ_MARGIN 0
+#define SPLITTER_HORZ_WIDTH 4
+#define SPLITTER_HORZ_RANGE_MIN 170
+#define SPLITTER_HORZ_RANGE_MAX 400
+
+
+// CChatWnd dialog
+
+IMPLEMENT_DYNAMIC(CChatWnd, CDialog)
+
+BEGIN_MESSAGE_MAP(CChatWnd, CResizableDialog)
+ ON_WM_KEYDOWN()
+ ON_WM_SHOWWINDOW()
+ ON_MESSAGE(UM_CLOSETAB, OnCloseTab)
+ ON_WM_SYSCOLORCHANGE()
+ ON_WM_CTLCOLOR()
+ ON_WM_CONTEXTMENU()
+ ON_WM_HELPINFO()
+ ON_NOTIFY(LVN_ITEMACTIVATE, IDC_FRIENDS_LIST, OnLvnItemActivateFriendList)
+ ON_NOTIFY(NM_CLICK, IDC_FRIENDS_LIST, OnNmClickFriendList)
+ ON_STN_DBLCLK(IDC_FRIENDSICON, OnStnDblClickFriendIcon)
+ ON_BN_CLICKED(IDC_CSEND, OnBnClickedSend)
+ ON_BN_CLICKED(IDC_CCLOSE, OnBnClickedClose)
+END_MESSAGE_MAP()
+
+CChatWnd::CChatWnd(CWnd* pParent /*=NULL*/)
+ : CResizableDialog(CChatWnd::IDD, pParent)
+{
+ icon_friend = NULL;
+ icon_msg = NULL;
+ m_pwndSmileySel = NULL;
+}
+
+CChatWnd::~CChatWnd()
+{
+ if (m_pwndSmileySel != NULL){
+ m_pwndSmileySel->DestroyWindow();
+ delete m_pwndSmileySel;
+ }
+ if (icon_friend)
+ VERIFY( DestroyIcon(icon_friend) );
+ if (icon_msg)
+ VERIFY( DestroyIcon(icon_msg) );
+}
+
+void CChatWnd::DoDataExchange(CDataExchange* pDX)
+{
+ CResizableDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_CHATSEL, chatselector);
+ DDX_Control(pDX, IDC_FRIENDS_LIST, m_FriendListCtrl);
+ DDX_Control(pDX, IDC_FRIENDS_MSG, m_cUserInfo);
+ DDX_Control(pDX, IDC_TEXT_FORMAT, m_wndFormat);
+ DDX_Control(pDX, IDC_CMESSAGE, m_wndMessage);
+ DDX_Control(pDX, IDC_CSEND, m_wndSend);
+ DDX_Control(pDX, IDC_CCLOSE, m_wndClose);
+}
+
+void CChatWnd::OnLvnItemActivateFriendList(NMHDR* /*pNMHDR*/, LRESULT* /*pResult*/)
+{
+ UpdateSelectedFriendMsgDetails();
+}
+
+void CChatWnd::UpdateSelectedFriendMsgDetails()
+{
+ int iSel = m_FriendListCtrl.GetSelectionMark();
+ if (iSel != -1) {
+ CFriend* pFriend = (CFriend*)m_FriendListCtrl.GetItemData(iSel);
+ ShowFriendMsgDetails(pFriend);
+ }
+ else
+ ShowFriendMsgDetails(NULL);
+}
+
+void CChatWnd::ShowFriendMsgDetails(CFriend* pFriend)
+{
+ if (pFriend)
+ {
+ CString buffer;
+
+ // Name
+ if (pFriend->GetLinkedClient())
+ GetDlgItem(IDC_FRIENDS_NAME_EDIT)->SetWindowText(pFriend->GetLinkedClient()->GetUserName());
+ else if (pFriend->m_strName != _T(""))
+ GetDlgItem(IDC_FRIENDS_NAME_EDIT)->SetWindowText(pFriend->m_strName);
+ else
+ GetDlgItem(IDC_FRIENDS_NAME_EDIT)->SetWindowText(_T("?"));
+
+ // Hash
+ if (pFriend->GetLinkedClient())
+ GetDlgItem(IDC_FRIENDS_USERHASH_EDIT)->SetWindowText(md4str(pFriend->GetLinkedClient()->GetUserHash()));
+ else if (pFriend->HasUserhash())
+ GetDlgItem(IDC_FRIENDS_USERHASH_EDIT)->SetWindowText(md4str(pFriend->m_abyUserhash));
+ else
+ GetDlgItem(IDC_FRIENDS_USERHASH_EDIT)->SetWindowText(_T("?"));
+
+ // Client
+ if (pFriend->GetLinkedClient())
+ GetDlgItem(IDC_FRIENDS_CLIENTE_EDIT)->SetWindowText(pFriend->GetLinkedClient()->GetClientSoftVer());
+ else
+ GetDlgItem(IDC_FRIENDS_CLIENTE_EDIT)->SetWindowText(_T("?"));
+
+ // Identification
+ if (pFriend->GetLinkedClient() && pFriend->GetLinkedClient()->Credits())
+ {
+ if (theApp.clientcredits->CryptoAvailable())
+ {
+ switch (pFriend->GetLinkedClient()->Credits()->GetCurrentIdentState(pFriend->GetLinkedClient()->GetIP()))
+ {
+ case IS_NOTAVAILABLE:
+ GetDlgItem(IDC_FRIENDS_IDENTIFICACION_EDIT)->SetWindowText(GetResString(IDS_IDENTNOSUPPORT));
+ break;
+ case IS_IDFAILED:
+ case IS_IDNEEDED:
+ case IS_IDBADGUY:
+ GetDlgItem(IDC_FRIENDS_IDENTIFICACION_EDIT)->SetWindowText(GetResString(IDS_IDENTFAILED));
+ break;
+ case IS_IDENTIFIED:
+ GetDlgItem(IDC_FRIENDS_IDENTIFICACION_EDIT)->SetWindowText(GetResString(IDS_IDENTOK));
+ break;
+ }
+ }
+ else
+ GetDlgItem(IDC_FRIENDS_IDENTIFICACION_EDIT)->SetWindowText(GetResString(IDS_IDENTNOSUPPORT));
+ }
+ else
+ GetDlgItem(IDC_FRIENDS_IDENTIFICACION_EDIT)->SetWindowText(_T("?"));
+
+ // Upload and downloaded
+ if (pFriend->GetLinkedClient() && pFriend->GetLinkedClient()->Credits())
+ GetDlgItem(IDC_FRIENDS_DESCARGADO_EDIT)->SetWindowText(CastItoXBytes(pFriend->GetLinkedClient()->Credits()->GetDownloadedTotal(), false, false));
+ else
+ GetDlgItem(IDC_FRIENDS_DESCARGADO_EDIT)->SetWindowText(_T("?"));
+
+ if (pFriend->GetLinkedClient() && pFriend->GetLinkedClient()->Credits())
+ GetDlgItem(IDC_FRIENDS_SUBIDO_EDIT)->SetWindowText(CastItoXBytes(pFriend->GetLinkedClient()->Credits()->GetUploadedTotal(), false, false));
+ else
+ GetDlgItem(IDC_FRIENDS_SUBIDO_EDIT)->SetWindowText(_T("?"));
+ }
+ else
+ {
+ GetDlgItem(IDC_FRIENDS_NAME_EDIT)->SetWindowText(_T("-"));
+ GetDlgItem(IDC_FRIENDS_USERHASH_EDIT)->SetWindowText(_T("-"));
+ GetDlgItem(IDC_FRIENDS_CLIENTE_EDIT)->SetWindowText(_T("-"));
+ GetDlgItem(IDC_FRIENDS_IDENTIFICACION_EDIT)->SetWindowText(_T("-"));
+ GetDlgItem(IDC_FRIENDS_DESCARGADO_EDIT)->SetWindowText(_T("-"));
+ GetDlgItem(IDC_FRIENDS_SUBIDO_EDIT)->SetWindowText(_T("-"));
+ }
+}
+
+BOOL CChatWnd::OnInitDialog()
+{
+ CResizableDialog::OnInitDialog();
+ InitWindowStyles(this);
+ SetAllIcons();
+
+ m_wndMessage.SetLimitText(MAX_CLIENT_MSG_LEN);
+ if (theApp.m_fontChatEdit.m_hObject)
+ {
+ m_wndMessage.SendMessage(WM_SETFONT, (WPARAM)theApp.m_fontChatEdit.m_hObject, FALSE);
+ CRect rcEdit;
+ m_wndMessage.GetWindowRect(&rcEdit);
+ ScreenToClient(&rcEdit);
+ rcEdit.top -= 2;
+ rcEdit.bottom += 2;
+ m_wndMessage.MoveWindow(&rcEdit, FALSE);
+ }
+
+ chatselector.Init(this);
+ m_FriendListCtrl.Init();
+
+ CRect rcSpl;
+ m_FriendListCtrl.GetWindowRect(rcSpl);
+ ScreenToClient(rcSpl);
+ rcSpl.left = rcSpl.right + SPLITTER_HORZ_MARGIN;
+ rcSpl.right = rcSpl.left + SPLITTER_HORZ_WIDTH;
+ m_wndSplitterHorz.Create(WS_CHILD | WS_VISIBLE, rcSpl, this, IDC_SPLITTER_FRIEND);
+
+ // Vista: Remove the TBSTYLE_TRANSPARENT to avoid flickering (can be done only after the toolbar was initially created with TBSTYLE_TRANSPARENT !?)
+ m_wndFormat.ModifyStyle((theApp.m_ullComCtrlVer >= MAKEDLLVERULL(6, 16, 0, 0)) ? TBSTYLE_TRANSPARENT : 0, TBSTYLE_TOOLTIPS);
+ m_wndFormat.SetExtendedStyle(m_wndFormat.GetExtendedStyle() | TBSTYLE_EX_MIXEDBUTTONS);
+ TBBUTTON atb[1] = {0};
+ atb[0].iBitmap = 0;
+ atb[0].idCommand = IDC_SMILEY;
+ atb[0].fsState = TBSTATE_ENABLED;
+ atb[0].fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE;
+ atb[0].iString = -1;
+ m_wndFormat.AddButtons(_countof(atb), atb);
+
+ CSize size;
+ m_wndFormat.GetMaxSize(&size);
+ if (size.cx < 24) // avoid glitch with COMCTL32 v5.81 and Win2000
+ size.cx = 24;
+ if (size.cy < 22) // avoid glitch with COMCTL32 v5.81 and Win2000
+ size.cy = 22;
+ ::SetWindowPos(m_wndFormat, NULL, 0, 0, size.cx, size.cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
+
+ AddAnchor(IDC_FRIENDSICON, TOP_LEFT);
+ AddAnchor(IDC_FRIENDS_LBL, TOP_LEFT);
+ AddAnchor(IDC_FRIENDS_NAME, BOTTOM_LEFT);
+ AddAnchor(IDC_FRIENDS_USERHASH, BOTTOM_LEFT);
+ AddAnchor(IDC_FRIENDS_CLIENT, BOTTOM_LEFT);
+ AddAnchor(IDC_FRIENDS_IDENT, BOTTOM_LEFT);
+ AddAnchor(IDC_FRIENDS_UPLOADED, BOTTOM_LEFT);
+ AddAnchor(IDC_FRIENDS_DOWNLOADED, BOTTOM_LEFT);
+ AddAnchor(m_wndSplitterHorz, TOP_LEFT, BOTTOM_LEFT);
+ AddAnchor(m_wndFormat, BOTTOM_LEFT);
+ AddAnchor(m_wndMessage, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(m_wndSend, BOTTOM_RIGHT);
+ AddAnchor(m_wndClose, BOTTOM_RIGHT);
+
+ int iPosStatInit = rcSpl.left;
+ int iPosStatNew = thePrefs.GetSplitterbarPositionFriend();
+ if (iPosStatNew > SPLITTER_HORZ_RANGE_MAX)
+ iPosStatNew = SPLITTER_HORZ_RANGE_MAX;
+ else if (iPosStatNew < SPLITTER_HORZ_RANGE_MIN)
+ iPosStatNew = SPLITTER_HORZ_RANGE_MIN;
+ rcSpl.left = iPosStatNew;
+ rcSpl.right = iPosStatNew + SPLITTER_HORZ_WIDTH;
+ if (iPosStatNew != iPosStatInit)
+ {
+ m_wndSplitterHorz.MoveWindow(rcSpl);
+ DoResize(iPosStatNew - iPosStatInit);
+ }
+
+ Localize();
+ theApp.friendlist->ShowFriends();
+
+ return TRUE;
+}
+
+void CChatWnd::DoResize(int iDelta)
+{
+ CSplitterControl::ChangeWidth(&m_FriendListCtrl, iDelta);
+ m_FriendListCtrl.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
+ CSplitterControl::ChangeWidth(&m_cUserInfo, iDelta);
+ CSplitterControl::ChangeWidth(GetDlgItem(IDC_FRIENDS_NAME_EDIT), iDelta);
+ CSplitterControl::ChangeWidth(GetDlgItem(IDC_FRIENDS_USERHASH_EDIT), iDelta);
+ CSplitterControl::ChangeWidth(GetDlgItem(IDC_FRIENDS_CLIENTE_EDIT), iDelta);
+ CSplitterControl::ChangeWidth(GetDlgItem(IDC_FRIENDS_IDENTIFICACION_EDIT), iDelta);
+ CSplitterControl::ChangeWidth(GetDlgItem(IDC_FRIENDS_SUBIDO_EDIT), iDelta);
+ CSplitterControl::ChangeWidth(GetDlgItem(IDC_FRIENDS_DESCARGADO_EDIT), iDelta);
+ CSplitterControl::ChangeWidth(&chatselector, -iDelta, CW_RIGHTALIGN);
+ CSplitterControl::ChangePos(GetDlgItem(IDC_MESSAGES_LBL), -iDelta, 0);
+ CSplitterControl::ChangePos(GetDlgItem(IDC_MESSAGEICON), -iDelta, 0);
+ CSplitterControl::ChangePos(&m_wndFormat, -iDelta, 0);
+ CSplitterControl::ChangePos(&m_wndMessage, -iDelta, 0);
+ CSplitterControl::ChangeWidth(&m_wndMessage, -iDelta);
+
+ CRect rcSpl;
+ m_wndSplitterHorz.GetWindowRect(rcSpl);
+ ScreenToClient(rcSpl);
+ thePrefs.SetSplitterbarPositionFriend(rcSpl.left);
+
+ RemoveAnchor(m_FriendListCtrl);
+ AddAnchor(m_FriendListCtrl, TOP_LEFT, BOTTOM_LEFT);
+ RemoveAnchor(m_cUserInfo);
+ AddAnchor(m_cUserInfo, BOTTOM_LEFT, BOTTOM_LEFT);
+ RemoveAnchor(chatselector);
+ AddAnchor(chatselector, TOP_LEFT, BOTTOM_RIGHT);
+ RemoveAnchor(IDC_MESSAGES_LBL);
+ AddAnchor(IDC_MESSAGES_LBL, TOP_LEFT);
+ RemoveAnchor(IDC_MESSAGEICON);
+ AddAnchor(IDC_MESSAGEICON, TOP_LEFT);
+ RemoveAnchor(IDC_FRIENDS_NAME_EDIT);
+ AddAnchor(IDC_FRIENDS_NAME_EDIT, BOTTOM_LEFT);
+ RemoveAnchor(IDC_FRIENDS_USERHASH_EDIT);
+ AddAnchor(IDC_FRIENDS_USERHASH_EDIT, BOTTOM_LEFT);
+ RemoveAnchor(IDC_FRIENDS_CLIENTE_EDIT);
+ AddAnchor(IDC_FRIENDS_CLIENTE_EDIT, BOTTOM_LEFT);
+ RemoveAnchor(IDC_FRIENDS_IDENTIFICACION_EDIT);
+ AddAnchor(IDC_FRIENDS_IDENTIFICACION_EDIT, BOTTOM_LEFT);
+ RemoveAnchor(IDC_FRIENDS_SUBIDO_EDIT);
+ AddAnchor(IDC_FRIENDS_SUBIDO_EDIT, BOTTOM_LEFT);
+ RemoveAnchor(IDC_FRIENDS_DESCARGADO_EDIT);
+ AddAnchor(IDC_FRIENDS_DESCARGADO_EDIT, BOTTOM_LEFT);
+ RemoveAnchor(m_wndSplitterHorz);
+ AddAnchor(m_wndSplitterHorz, TOP_LEFT, BOTTOM_LEFT);
+ RemoveAnchor(m_wndFormat);
+ AddAnchor(m_wndFormat, BOTTOM_LEFT);
+ RemoveAnchor(m_wndMessage);
+ AddAnchor(m_wndMessage, BOTTOM_LEFT, BOTTOM_RIGHT);
+ RemoveAnchor(m_wndSend);
+ AddAnchor(m_wndSend, BOTTOM_RIGHT);
+ RemoveAnchor(m_wndClose);
+ AddAnchor(m_wndClose, BOTTOM_RIGHT);
+
+ CRect rcWnd;
+ GetWindowRect(rcWnd);
+ ScreenToClient(rcWnd);
+ m_wndSplitterHorz.SetRange(rcWnd.left + SPLITTER_HORZ_RANGE_MIN + SPLITTER_HORZ_WIDTH/2,
+ rcWnd.left + SPLITTER_HORZ_RANGE_MAX - SPLITTER_HORZ_WIDTH/2);
+
+ Invalidate();
+ UpdateWindow();
+}
+
+LRESULT CChatWnd::DefWindowProc(UINT uMessage, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMessage)
+ {
+ case WM_PAINT:
+ if (m_wndSplitterHorz)
+ {
+ CRect rcWnd;
+ GetWindowRect(rcWnd);
+ if (rcWnd.Width() > 0)
+ {
+ CRect rcSpl;
+ m_FriendListCtrl.GetWindowRect(rcSpl);
+ ScreenToClient(rcSpl);
+ rcSpl.left = rcSpl.right + SPLITTER_HORZ_MARGIN;
+ rcSpl.right = rcSpl.left + SPLITTER_HORZ_WIDTH;
+ ScreenToClient(rcWnd);
+ rcSpl.bottom = rcWnd.bottom - 6;
+ m_wndSplitterHorz.MoveWindow(rcSpl, TRUE);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (wParam == IDC_SPLITTER_FRIEND)
+ {
+ SPC_NMHDR* pHdr = (SPC_NMHDR*)lParam;
+ DoResize(pHdr->delta);
+ }
+ break;
+
+ case WM_SIZE:
+ if (m_wndSplitterHorz)
+ {
+ CRect rcWnd;
+ GetWindowRect(rcWnd);
+ ScreenToClient(rcWnd);
+ m_wndSplitterHorz.SetRange(rcWnd.left + SPLITTER_HORZ_RANGE_MIN + SPLITTER_HORZ_WIDTH/2,
+ rcWnd.left + SPLITTER_HORZ_RANGE_MAX - SPLITTER_HORZ_WIDTH/2);
+ }
+ break;
+ }
+ return CResizableDialog::DefWindowProc(uMessage, wParam, lParam);
+}
+
+void CChatWnd::StartSession(CUpDownClient* client)
+{
+ if (!client->GetUserName())
+ return;
+ theApp.emuledlg->SetActiveDialog(this);
+ chatselector.StartSession(client, true);
+}
+
+void CChatWnd::OnShowWindow(BOOL bShow, UINT /*nStatus*/)
+{
+ if (bShow)
+ chatselector.ShowChat();
+}
+
+BOOL CChatWnd::PreTranslateMessage(MSG* pMsg)
+{
+ if (pMsg->message == WM_KEYDOWN)
+ {
+ // Don't handle Ctrl+Tab in this window. It will be handled by main window.
+ if (pMsg->wParam == VK_TAB && GetAsyncKeyState(VK_CONTROL) < 0)
+ return FALSE;
+
+ if (pMsg->wParam == VK_RETURN){
+ if (pMsg->hwnd == m_wndMessage)
+ OnBnClickedSend();
+ }
+
+ if (pMsg->hwnd == m_wndMessage && (pMsg->wParam == VK_UP || pMsg->wParam == VK_DOWN)){
+ ScrollHistory(pMsg->wParam == VK_DOWN);
+ return TRUE;
+ }
+ }
+ else if (pMsg->message == WM_KEYUP)
+ {
+ if (pMsg->hwnd == m_FriendListCtrl.m_hWnd)
+ OnLvnItemActivateFriendList(0, 0);
+ }
+
+ return CResizableDialog::PreTranslateMessage(pMsg);
+}
+
+void CChatWnd::OnNmClickFriendList(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ OnLvnItemActivateFriendList(pNMHDR, pResult);
+ *pResult = 0;
+}
+
+void CChatWnd::SetAllIcons()
+{
+ if (icon_friend)
+ VERIFY( DestroyIcon(icon_friend) );
+ if (icon_msg)
+ VERIFY( DestroyIcon(icon_msg) );
+ icon_friend = theApp.LoadIcon(_T("Friend"), 16, 16);
+ icon_msg = theApp.LoadIcon(_T("Message"), 16, 16);
+ ((CStatic*)GetDlgItem(IDC_MESSAGEICON))->SetIcon(icon_msg);
+ ((CStatic*)GetDlgItem(IDC_FRIENDSICON))->SetIcon(icon_friend);
+ m_cUserInfo.SetIcon(_T("Info"));
+
+ CImageList iml;
+ iml.Create(16, 16, theApp.m_iDfltImageListColorFlags | ILC_MASK, 0, 1);
+ iml.Add(CTempIconLoader(_T("Smiley_Smile")));
+ CImageList *pimlOld = m_wndFormat.SetImageList(&iml);
+ iml.Detach();
+ if (pimlOld)
+ pimlOld->DeleteImageList();
+}
+
+void CChatWnd::Localize()
+{
+ GetDlgItem(IDC_FRIENDS_LBL)->SetWindowText(GetResString(IDS_CW_FRIENDS));
+ GetDlgItem(IDC_MESSAGES_LBL)->SetWindowText(GetResString(IDS_CW_MESSAGES));
+ m_cUserInfo.SetWindowText(GetResString(IDS_INFO));
+ GetDlgItem(IDC_FRIENDS_DOWNLOADED)->SetWindowText(GetResString(IDS_CHAT_DOWNLOADED));
+ GetDlgItem(IDC_FRIENDS_UPLOADED)->SetWindowText(GetResString(IDS_CHAT_UPLOADED));
+ GetDlgItem(IDC_FRIENDS_IDENT)->SetWindowText(GetResString(IDS_CHAT_IDENT));
+ GetDlgItem(IDC_FRIENDS_CLIENT)->SetWindowText(GetResString(IDS_CD_CSOFT));
+ GetDlgItem(IDC_FRIENDS_NAME)->SetWindowText(GetResString(IDS_CD_UNAME));
+ GetDlgItem(IDC_FRIENDS_USERHASH)->SetWindowText(GetResString(IDS_CD_UHASH));
+ m_wndSend.SetWindowText(GetResString(IDS_CW_SEND));
+ m_wndClose.SetWindowText(GetResString(IDS_CW_CLOSE));
+ m_wndFormat.SetBtnText(IDC_SMILEY, _T("Smileys"));
+ m_FriendListCtrl.Localize();
+}
+
+LRESULT CChatWnd::OnCloseTab(WPARAM wParam, LPARAM /*lParam*/)
+{
+ TCITEM item = {0};
+ item.mask = TCIF_PARAM;
+ if (chatselector.GetItem((int)wParam, &item))
+ chatselector.EndSession(((CChatItem*)item.lParam)->client);
+ return TRUE;
+}
+
+void CChatWnd::ScrollHistory(bool down)
+{
+ CChatItem* ci = chatselector.GetCurrentChatItem();
+ if (ci == NULL)
+ return;
+
+ if ((ci->history_pos == 0 && !down) || (ci->history_pos == ci->history.GetCount() && down))
+ return;
+ if (down)
+ ++ci->history_pos;
+ else
+ --ci->history_pos;
+
+ CString strBuffer = (ci->history_pos == ci->history.GetCount()) ? _T("") : ci->history.GetAt(ci->history_pos);
+ m_wndMessage.SetWindowText(strBuffer);
+ m_wndMessage.SetSel(strBuffer.GetLength(), strBuffer.GetLength());
+}
+
+void CChatWnd::OnSysColorChange()
+{
+ CResizableDialog::OnSysColorChange();
+ SetAllIcons();
+}
+
+void CChatWnd::UpdateFriendlistCount(UINT count)
+{
+ CString strTemp;
+ strTemp.Format(_T(" (%i)"), count);
+ GetDlgItem(IDC_FRIENDS_LBL)->SetWindowText(GetResString(IDS_CW_FRIENDS) + strTemp);
+}
+
+BOOL CChatWnd::OnHelpInfo(HELPINFO* /*pHelpInfo*/)
+{
+ theApp.ShowHelp(eMule_FAQ_GUI_Messages);
+ return TRUE;
+}
+
+void CChatWnd::OnStnDblClickFriendIcon()
+{
+ theApp.emuledlg->ShowPreferences(IDD_PPG_MESSAGES);
+}
+
+BOOL CChatWnd::OnCommand(WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam) {
+ case IDC_SMILEY:
+ OnBnClickedSmiley();
+ break;
+ }
+ return CResizableDialog::OnCommand(wParam, lParam);
+}
+
+void CChatWnd::OnBnClickedSmiley()
+{
+ if (m_pwndSmileySel) {
+ m_pwndSmileySel->DestroyWindow();
+ delete m_pwndSmileySel;
+ m_pwndSmileySel = NULL;
+ }
+ m_pwndSmileySel = new CSmileySelector;
+
+ CRect rcBtn;
+ m_wndFormat.GetWindowRect(&rcBtn);
+ rcBtn.top -= 2;
+
+ if (!m_pwndSmileySel->Create(this, &rcBtn, &m_wndMessage))
+ {
+ delete m_pwndSmileySel;
+ m_pwndSmileySel = NULL;
+ }
+}
+
+void CChatWnd::OnBnClickedClose()
+{
+ chatselector.EndSession();
+}
+
+void CChatWnd::OnBnClickedSend()
+{
+ CString strMessage;
+ m_wndMessage.GetWindowText(strMessage);
+ strMessage.Trim();
+ if (!strMessage.IsEmpty())
+ {
+ if (chatselector.SendMessage(strMessage))
+ m_wndMessage.SetWindowText(_T(""));
+ }
+
+ m_wndMessage.SetFocus();
+}
+
+HBRUSH CChatWnd::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = theApp.emuledlg->GetCtlColor(pDC, pWnd, nCtlColor);
+ if (hbr)
+ return hbr;
+ return __super::OnCtlColor(pDC, pWnd, nCtlColor);
+}
diff --git a/ChatWnd.h b/ChatWnd.h
new file mode 100644
index 00000000..0d443d75
--- /dev/null
+++ b/ChatWnd.h
@@ -0,0 +1,82 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "ResizableLib\ResizableDialog.h"
+#include "ChatSelector.h"
+#include "FriendListCtrl.h"
+#include "SplitterControl.h"
+#include "IconStatic.h"
+#include "ToolBarCtrlX.h"
+
+class CSmileySelector;
+
+class CChatWnd : public CResizableDialog
+{
+ DECLARE_DYNAMIC(CChatWnd)
+
+public:
+ CChatWnd(CWnd* pParent = NULL); // standard constructor
+ virtual ~CChatWnd();
+
+// Dialog Data
+ enum { IDD = IDD_CHAT };
+
+ void StartSession(CUpDownClient* client);
+ void Localize();
+ void UpdateFriendlistCount(UINT count);
+ void UpdateSelectedFriendMsgDetails();
+ void ScrollHistory(bool down);
+ void EnableSmileys(bool bEnable) { chatselector.EnableSmileys(bEnable); }
+
+ CFriendListCtrl m_FriendListCtrl;
+ CChatSelector chatselector;
+
+protected:
+ friend class CChatSelector;
+ HICON icon_friend;
+ HICON icon_msg;
+ CSplitterControl m_wndSplitterHorz;
+ CIconStatic m_cUserInfo;
+ CToolBarCtrlX m_wndFormat;
+ CEdit m_wndMessage;
+ CButton m_wndSend;
+ CButton m_wndClose;
+ CSmileySelector *m_pwndSmileySel;
+
+ void SetAllIcons();
+ void DoResize(int delta);
+ void ShowFriendMsgDetails(CFriend* pFriend);
+
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual BOOL OnInitDialog();
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg LRESULT OnCloseTab(WPARAM wparam, LPARAM lparam);
+ afx_msg void OnBnClickedClose();
+ afx_msg void OnBnClickedSend();
+ afx_msg void OnBnClickedSmiley();
+ afx_msg void OnLvnItemActivateFriendList(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnNmClickFriendList(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
+ afx_msg void OnStnDblClickFriendIcon();
+ afx_msg void OnSysColorChange();
+};
diff --git a/ClientCredits.cpp b/ClientCredits.cpp
new file mode 100644
index 00000000..4c75b0f8
--- /dev/null
+++ b/ClientCredits.cpp
@@ -0,0 +1,701 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include
+#include "emule.h"
+#include "ClientCredits.h"
+#include "OtherFunctions.h"
+#include "Preferences.h"
+#include "SafeFile.h"
+#include "Opcodes.h"
+#include "Sockets.h"
+#pragma warning(disable:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+#pragma warning(disable:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4702) // unreachable code
+#include
+#include
+#include
+#include
+#pragma warning(default:4702) // unreachable code
+#pragma warning(default:4100) // unreferenced formal parameter
+#pragma warning(default:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(default:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+#include "emuledlg.h"
+#include "Log.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define CLIENTS_MET_FILENAME _T("clients.met")
+
+CClientCredits::CClientCredits(CreditStruct* in_credits)
+{
+ m_pCredits = in_credits;
+ InitalizeIdent();
+ m_dwUnSecureWaitTime = 0;
+ m_dwSecureWaitTime = 0;
+ m_dwWaitTimeIP = 0;
+}
+
+CClientCredits::CClientCredits(const uchar* key)
+{
+ m_pCredits = new CreditStruct;
+ memset(m_pCredits, 0, sizeof(CreditStruct));
+ md4cpy(m_pCredits->abyKey, key);
+ InitalizeIdent();
+ m_dwUnSecureWaitTime = ::GetTickCount();
+ m_dwSecureWaitTime = ::GetTickCount();
+ m_dwWaitTimeIP = 0;
+}
+
+CClientCredits::~CClientCredits()
+{
+ delete m_pCredits;
+}
+
+void CClientCredits::AddDownloaded(uint32 bytes, uint32 dwForIP) {
+ if ((GetCurrentIdentState(dwForIP) == IS_IDFAILED || GetCurrentIdentState(dwForIP) == IS_IDBADGUY || GetCurrentIdentState(dwForIP) == IS_IDNEEDED) && theApp.clientcredits->CryptoAvailable()) {
+ return;
+ }
+
+ //encode
+ uint64 current = (((uint64)m_pCredits->nDownloadedHi << 32) | m_pCredits->nDownloadedLo) + bytes;
+
+ //recode
+ m_pCredits->nDownloadedLo = (uint32)current;
+ m_pCredits->nDownloadedHi = (uint32)(current >> 32);
+}
+
+void CClientCredits::AddUploaded(uint32 bytes, uint32 dwForIP) {
+ if ((GetCurrentIdentState(dwForIP) == IS_IDFAILED || GetCurrentIdentState(dwForIP) == IS_IDBADGUY || GetCurrentIdentState(dwForIP) == IS_IDNEEDED) && theApp.clientcredits->CryptoAvailable()) {
+ return;
+ }
+
+ //encode
+ uint64 current = (((uint64)m_pCredits->nUploadedHi << 32) | m_pCredits->nUploadedLo) + bytes;
+
+ //recode
+ m_pCredits->nUploadedLo = (uint32)current;
+ m_pCredits->nUploadedHi = (uint32)(current >> 32);
+}
+
+uint64 CClientCredits::GetUploadedTotal() const {
+ return ((uint64)m_pCredits->nUploadedHi << 32) | m_pCredits->nUploadedLo;
+}
+
+uint64 CClientCredits::GetDownloadedTotal() const {
+ return ((uint64)m_pCredits->nDownloadedHi << 32) | m_pCredits->nDownloadedLo;
+}
+
+float CClientCredits::GetScoreRatio(uint32 dwForIP) const
+{
+ // check the client ident status
+ if ( ( GetCurrentIdentState(dwForIP) == IS_IDFAILED || GetCurrentIdentState(dwForIP) == IS_IDBADGUY || GetCurrentIdentState(dwForIP) == IS_IDNEEDED) && theApp.clientcredits->CryptoAvailable() ){
+ // bad guy - no credits for you
+ return 1.0F;
+ }
+
+ if (GetDownloadedTotal() < 1048576)
+ return 1.0F;
+ float result = 0.0F;
+ if (!GetUploadedTotal())
+ result = 10.0F;
+ else
+ result = (float)(((double)GetDownloadedTotal()*2.0)/(double)GetUploadedTotal());
+
+ // exponential calcualtion of the max multiplicator based on uploaded data (9.2MB = 3.34, 100MB = 10.0)
+ float result2 = 0.0F;
+ result2 = (float)(GetDownloadedTotal()/1048576.0);
+ result2 += 2.0F;
+ result2 = (float)sqrt(result2);
+
+ // linear calcualtion of the max multiplicator based on uploaded data for the first chunk (1MB = 1.01, 9.2MB = 3.34)
+ float result3 = 10.0F;
+ if (GetDownloadedTotal() < 9646899){
+ result3 = (((float)(GetDownloadedTotal() - 1048576) / 8598323.0F) * 2.34F) + 1.0F;
+ }
+
+ // take the smallest result
+ result = min(result, min(result2, result3));
+
+ if (result < 1.0F)
+ return 1.0F;
+ else if (result > 10.0F)
+ return 10.0F;
+ return result;
+}
+
+
+CClientCreditsList::CClientCreditsList()
+{
+ m_nLastSaved = ::GetTickCount();
+ LoadList();
+
+ InitalizeCrypting();
+}
+
+CClientCreditsList::~CClientCreditsList()
+{
+ SaveList();
+ CClientCredits* cur_credit;
+ CCKey tmpkey(0);
+ POSITION pos = m_mapClients.GetStartPosition();
+ while (pos){
+ m_mapClients.GetNextAssoc(pos, tmpkey, cur_credit);
+ delete cur_credit;
+ }
+ delete m_pSignkey;
+}
+
+void CClientCreditsList::LoadList()
+{
+ CString strFileName = thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + CLIENTS_MET_FILENAME;
+ const int iOpenFlags = CFile::modeRead|CFile::osSequentialScan|CFile::typeBinary|CFile::shareDenyWrite;
+ CSafeBufferedFile file;
+ CFileException fexp;
+ if (!file.Open(strFileName, iOpenFlags, &fexp)){
+ if (fexp.m_cause != CFileException::fileNotFound){
+ CString strError(GetResString(IDS_ERR_LOADCREDITFILE));
+ TCHAR szError[MAX_CFEXP_ERRORMSG];
+ if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){
+ strError += _T(" - ");
+ strError += szError;
+ }
+ LogError(LOG_STATUSBAR, _T("%s"), strError);
+ }
+ return;
+ }
+ setvbuf(file.m_pStream, NULL, _IOFBF, 16384);
+
+ try{
+ uint8 version = file.ReadUInt8();
+ if (version != CREDITFILE_VERSION && version != CREDITFILE_VERSION_29){
+ LogWarning(GetResString(IDS_ERR_CREDITFILEOLD));
+ file.Close();
+ return;
+ }
+
+ // everything is ok, lets see if the backup exist...
+ CString strBakFileName;
+ strBakFileName.Format(_T("%s") CLIENTS_MET_FILENAME _T(".bak"), thePrefs.GetMuleDirectory(EMULE_CONFIGDIR));
+
+ DWORD dwBakFileSize = 0;
+ BOOL bCreateBackup = TRUE;
+
+ HANDLE hBakFile = ::CreateFile(strBakFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hBakFile != INVALID_HANDLE_VALUE)
+ {
+ // Ok, the backup exist, get the size
+ dwBakFileSize = ::GetFileSize(hBakFile, NULL); //debug
+ if (dwBakFileSize > (DWORD)file.GetLength())
+ {
+ // the size of the backup was larger then the org. file, something is wrong here, don't overwrite old backup..
+ bCreateBackup = FALSE;
+ }
+ //else: backup is smaller or the same size as org. file, proceed with copying of file
+ ::CloseHandle(hBakFile);
+ }
+ //else: the backup doesn't exist, create it
+
+ if (bCreateBackup)
+ {
+ file.Close(); // close the file before copying
+
+ if (!::CopyFile(strFileName, strBakFileName, FALSE))
+ LogError(GetResString(IDS_ERR_MAKEBAKCREDITFILE));
+
+ // reopen file
+ CFileException fexp;
+ if (!file.Open(strFileName, iOpenFlags, &fexp)){
+ CString strError(GetResString(IDS_ERR_LOADCREDITFILE));
+ TCHAR szError[MAX_CFEXP_ERRORMSG];
+ if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){
+ strError += _T(" - ");
+ strError += szError;
+ }
+ LogError(LOG_STATUSBAR, _T("%s"), strError);
+ return;
+ }
+ setvbuf(file.m_pStream, NULL, _IOFBF, 16384);
+ file.Seek(1, CFile::begin); //set filepointer behind file version byte
+ }
+
+ UINT count = file.ReadUInt32();
+ m_mapClients.InitHashTable(count+5000); // TODO: should be prime number... and 20% larger
+
+ const uint32 dwExpired = time(NULL) - 12960000; // today - 150 day
+ uint32 cDeleted = 0;
+ for (UINT i = 0; i < count; i++){
+ CreditStruct* newcstruct = new CreditStruct;
+ memset(newcstruct, 0, sizeof(CreditStruct));
+ if (version == CREDITFILE_VERSION_29)
+ file.Read(newcstruct, sizeof(CreditStruct_29a));
+ else
+ file.Read(newcstruct, sizeof(CreditStruct));
+
+ if (newcstruct->nLastSeen < dwExpired){
+ cDeleted++;
+ delete newcstruct;
+ continue;
+ }
+
+ CClientCredits* newcredits = new CClientCredits(newcstruct);
+ m_mapClients.SetAt(CCKey(newcredits->GetKey()), newcredits);
+ }
+ file.Close();
+
+ if (cDeleted>0)
+ AddLogLine(false, GetResString(IDS_CREDITFILELOADED) + GetResString(IDS_CREDITSEXPIRED), count-cDeleted,cDeleted);
+ else
+ AddLogLine(false, GetResString(IDS_CREDITFILELOADED), count);
+ }
+ catch(CFileException* error){
+ if (error->m_cause == CFileException::endOfFile)
+ LogError(LOG_STATUSBAR, GetResString(IDS_CREDITFILECORRUPT));
+ else{
+ TCHAR buffer[MAX_CFEXP_ERRORMSG];
+ error->GetErrorMessage(buffer, ARRSIZE(buffer));
+ LogError(LOG_STATUSBAR, GetResString(IDS_ERR_CREDITFILEREAD), buffer);
+ }
+ error->Delete();
+ }
+}
+
+void CClientCreditsList::SaveList()
+{
+ if (thePrefs.GetLogFileSaving())
+ AddDebugLogLine(false, _T("Saving clients credit list file \"%s\""), CLIENTS_MET_FILENAME);
+ m_nLastSaved = ::GetTickCount();
+
+ CString name = thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + CLIENTS_MET_FILENAME;
+ CFile file;// no buffering needed here since we swap out the entire array
+ CFileException fexp;
+ if (!file.Open(name, CFile::modeWrite|CFile::modeCreate|CFile::typeBinary|CFile::shareDenyWrite, &fexp)){
+ CString strError(GetResString(IDS_ERR_FAILED_CREDITSAVE));
+ TCHAR szError[MAX_CFEXP_ERRORMSG];
+ if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){
+ strError += _T(" - ");
+ strError += szError;
+ }
+ LogError(LOG_STATUSBAR, _T("%s"), strError);
+ return;
+ }
+
+ uint32 count = m_mapClients.GetCount();
+ BYTE* pBuffer = new BYTE[count*sizeof(CreditStruct)];
+ CClientCredits* cur_credit;
+ CCKey tempkey(0);
+ POSITION pos = m_mapClients.GetStartPosition();
+ count = 0;
+ while (pos)
+ {
+ m_mapClients.GetNextAssoc(pos, tempkey, cur_credit);
+ if (cur_credit->GetUploadedTotal() || cur_credit->GetDownloadedTotal())
+ {
+ memcpy(pBuffer+(count*sizeof(CreditStruct)), cur_credit->GetDataStruct(), sizeof(CreditStruct));
+ count++;
+ }
+ }
+
+ try{
+ uint8 version = CREDITFILE_VERSION;
+ file.Write(&version, 1);
+ file.Write(&count, 4);
+ file.Write(pBuffer, count*sizeof(CreditStruct));
+ if (thePrefs.GetCommitFiles() >= 2 || (thePrefs.GetCommitFiles() >= 1 && !theApp.emuledlg->IsRunning()))
+ file.Flush();
+ file.Close();
+ }
+ catch(CFileException* error){
+ CString strError(GetResString(IDS_ERR_FAILED_CREDITSAVE));
+ TCHAR szError[MAX_CFEXP_ERRORMSG];
+ if (error->GetErrorMessage(szError, ARRSIZE(szError))){
+ strError += _T(" - ");
+ strError += szError;
+ }
+ LogError(LOG_STATUSBAR, _T("%s"), strError);
+ error->Delete();
+ }
+
+ delete[] pBuffer;
+}
+
+CClientCredits* CClientCreditsList::GetCredit(const uchar* key)
+{
+ CClientCredits* result;
+ CCKey tkey(key);
+ if (!m_mapClients.Lookup(tkey, result)){
+ result = new CClientCredits(key);
+ m_mapClients.SetAt(CCKey(result->GetKey()), result);
+ }
+ result->SetLastSeen();
+ return result;
+}
+
+void CClientCreditsList::Process()
+{
+ if (::GetTickCount() - m_nLastSaved > MIN2MS(13))
+ SaveList();
+}
+
+void CClientCredits::InitalizeIdent()
+{
+ if (m_pCredits->nKeySize == 0 ){
+ memset(m_abyPublicKey,0,80); // for debugging
+ m_nPublicKeyLen = 0;
+ IdentState = IS_NOTAVAILABLE;
+ }
+ else{
+ m_nPublicKeyLen = m_pCredits->nKeySize;
+ memcpy(m_abyPublicKey, m_pCredits->abySecureIdent, m_nPublicKeyLen);
+ IdentState = IS_IDNEEDED;
+ }
+ m_dwCryptRndChallengeFor = 0;
+ m_dwCryptRndChallengeFrom = 0;
+ m_dwIdentIP = 0;
+}
+
+void CClientCredits::Verified(uint32 dwForIP)
+{
+ m_dwIdentIP = dwForIP;
+ // client was verified, copy the keyto store him if not done already
+ if (m_pCredits->nKeySize == 0){
+ m_pCredits->nKeySize = m_nPublicKeyLen;
+ memcpy(m_pCredits->abySecureIdent, m_abyPublicKey, m_nPublicKeyLen);
+ if (GetDownloadedTotal() > 0){
+ // for security reason, we have to delete all prior credits here
+ m_pCredits->nDownloadedHi = 0;
+ m_pCredits->nDownloadedLo = 1;
+ m_pCredits->nUploadedHi = 0;
+ m_pCredits->nUploadedLo = 1; // in order to safe this client, set 1 byte
+ if (thePrefs.GetVerbose())
+ DEBUG_ONLY(AddDebugLogLine(false, _T("Credits deleted due to new SecureIdent")));
+ }
+ }
+ IdentState = IS_IDENTIFIED;
+}
+
+bool CClientCredits::SetSecureIdent(const uchar* pachIdent, uint8 nIdentLen) // verified Public key cannot change, use only if there is not public key yet
+{
+ if (MAXPUBKEYSIZE < nIdentLen || m_pCredits->nKeySize != 0 )
+ return false;
+ memcpy(m_abyPublicKey,pachIdent, nIdentLen);
+ m_nPublicKeyLen = nIdentLen;
+ IdentState = IS_IDNEEDED;
+ return true;
+}
+
+EIdentState CClientCredits::GetCurrentIdentState(uint32 dwForIP) const
+{
+ if (IdentState != IS_IDENTIFIED)
+ return IdentState;
+ else{
+ if (dwForIP == m_dwIdentIP)
+ return IS_IDENTIFIED;
+ else
+ return IS_IDBADGUY;
+ // mod note: clients which just reconnected after an IP change and have to ident yet will also have this state for 1-2 seconds
+ // so don't try to spam such clients with "bad guy" messages (besides: spam messages are always bad)
+ }
+}
+
+using namespace CryptoPP;
+
+void CClientCreditsList::InitalizeCrypting()
+{
+ m_nMyPublicKeyLen = 0;
+ memset(m_abyMyPublicKey,0,80); // not really needed; better for debugging tho
+ m_pSignkey = NULL;
+ if (!thePrefs.IsSecureIdentEnabled())
+ return;
+ // check if keyfile is there
+ bool bCreateNewKey = false;
+ HANDLE hKeyFile = ::CreateFile(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + _T("cryptkey.dat"), GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hKeyFile != INVALID_HANDLE_VALUE)
+ {
+ if (::GetFileSize(hKeyFile, NULL) == 0)
+ bCreateNewKey = true;
+ ::CloseHandle(hKeyFile);
+ }
+ else
+ bCreateNewKey = true;
+ if (bCreateNewKey)
+ CreateKeyPair();
+
+ // load key
+ try{
+ // load private key
+ FileSource filesource(CStringA(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + _T("cryptkey.dat")), true,new Base64Decoder);
+ m_pSignkey = new RSASSA_PKCS1v15_SHA_Signer(filesource);
+ // calculate and store public key
+ RSASSA_PKCS1v15_SHA_Verifier pubkey(*m_pSignkey);
+ ArraySink asink(m_abyMyPublicKey, 80);
+ pubkey.DEREncode(asink);
+ m_nMyPublicKeyLen = (uint8)asink.TotalPutLength();
+ asink.MessageEnd();
+ }
+ catch(...)
+ {
+ delete m_pSignkey;
+ m_pSignkey = NULL;
+ LogError(LOG_STATUSBAR, GetResString(IDS_CRYPT_INITFAILED));
+ ASSERT(0);
+ }
+ ASSERT( Debug_CheckCrypting() );
+}
+
+bool CClientCreditsList::CreateKeyPair()
+{
+ try{
+ AutoSeededRandomPool rng;
+ InvertibleRSAFunction privkey;
+ privkey.Initialize(rng,RSAKEYSIZE);
+
+ Base64Encoder privkeysink(new FileSink(CStringA(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + _T("cryptkey.dat"))));
+ privkey.DEREncode(privkeysink);
+ privkeysink.MessageEnd();
+
+ if (thePrefs.GetLogSecureIdent())
+ AddDebugLogLine(false, _T("Created new RSA keypair"));
+ }
+ catch(...)
+ {
+ if (thePrefs.GetVerbose())
+ AddDebugLogLine(false, _T("Failed to create new RSA keypair"));
+ ASSERT ( false );
+ return false;
+ }
+ return true;
+}
+
+uint8 CClientCreditsList::CreateSignature(CClientCredits* pTarget, uchar* pachOutput, uint8 nMaxSize,
+ uint32 ChallengeIP, uint8 byChaIPKind,
+ CryptoPP::RSASSA_PKCS1v15_SHA_Signer* sigkey)
+{
+ // sigkey param is used for debug only
+ if (sigkey == NULL)
+ sigkey = m_pSignkey;
+
+ // create a signature of the public key from pTarget
+ ASSERT( pTarget );
+ ASSERT( pachOutput );
+ uint8 nResult;
+ if ( !CryptoAvailable() )
+ return 0;
+ try{
+
+ SecByteBlock sbbSignature(sigkey->SignatureLength());
+ AutoSeededRandomPool rng;
+ byte abyBuffer[MAXPUBKEYSIZE+9];
+ uint32 keylen = pTarget->GetSecIDKeyLen();
+ memcpy(abyBuffer,pTarget->GetSecureIdent(),keylen);
+ // 4 additional bytes random data send from this client
+ uint32 challenge = pTarget->m_dwCryptRndChallengeFrom;
+ ASSERT ( challenge != 0 );
+ PokeUInt32(abyBuffer+keylen, challenge);
+ uint16 ChIpLen = 0;
+ if ( byChaIPKind != 0){
+ ChIpLen = 5;
+ PokeUInt32(abyBuffer+keylen+4, ChallengeIP);
+ PokeUInt8(abyBuffer+keylen+4+4, byChaIPKind);
+ }
+ sigkey->SignMessage(rng, abyBuffer ,keylen+4+ChIpLen , sbbSignature.begin());
+ ArraySink asink(pachOutput, nMaxSize);
+ asink.Put(sbbSignature.begin(), sbbSignature.size());
+ nResult = (uint8)asink.TotalPutLength();
+ }
+ catch(...)
+ {
+ ASSERT ( false );
+ nResult = 0;
+ }
+ return nResult;
+}
+
+bool CClientCreditsList::VerifyIdent(CClientCredits* pTarget, const uchar* pachSignature, uint8 nInputSize,
+ uint32 dwForIP, uint8 byChaIPKind)
+{
+ ASSERT( pTarget );
+ ASSERT( pachSignature );
+ if ( !CryptoAvailable() ){
+ pTarget->IdentState = IS_NOTAVAILABLE;
+ return false;
+ }
+ bool bResult;
+ try{
+ StringSource ss_Pubkey((byte*)pTarget->GetSecureIdent(),pTarget->GetSecIDKeyLen(),true,0);
+ RSASSA_PKCS1v15_SHA_Verifier pubkey(ss_Pubkey);
+ // 4 additional bytes random data send from this client +5 bytes v2
+ byte abyBuffer[MAXPUBKEYSIZE+9];
+ memcpy(abyBuffer,m_abyMyPublicKey,m_nMyPublicKeyLen);
+ uint32 challenge = pTarget->m_dwCryptRndChallengeFor;
+ ASSERT ( challenge != 0 );
+ PokeUInt32(abyBuffer+m_nMyPublicKeyLen, challenge);
+
+ // v2 security improvments (not supported by 29b, not used as default by 29c)
+ uint8 nChIpSize = 0;
+ if (byChaIPKind != 0){
+ nChIpSize = 5;
+ uint32 ChallengeIP = 0;
+ switch (byChaIPKind){
+ case CRYPT_CIP_LOCALCLIENT:
+ ChallengeIP = dwForIP;
+ break;
+ case CRYPT_CIP_REMOTECLIENT:
+ if (theApp.serverconnect->GetClientID() == 0 || theApp.serverconnect->IsLowID()){
+ if (thePrefs.GetLogSecureIdent())
+ AddDebugLogLine(false, _T("Warning: Maybe SecureHash Ident fails because LocalIP is unknown"));
+ ChallengeIP = theApp.serverconnect->GetLocalIP();
+ }
+ else
+ ChallengeIP = theApp.serverconnect->GetClientID();
+ break;
+ case CRYPT_CIP_NONECLIENT: // maybe not supported in future versions
+ ChallengeIP = 0;
+ break;
+ }
+ PokeUInt32(abyBuffer+m_nMyPublicKeyLen+4, ChallengeIP);
+ PokeUInt8(abyBuffer+m_nMyPublicKeyLen+4+4, byChaIPKind);
+ }
+ //v2 end
+
+ bResult = pubkey.VerifyMessage(abyBuffer, m_nMyPublicKeyLen+4+nChIpSize, pachSignature, nInputSize);
+ }
+ catch(...)
+ {
+ if (thePrefs.GetVerbose())
+ AddDebugLogLine(false, _T("Error: Unknown exception in %hs"), __FUNCTION__);
+ //ASSERT(0);
+ bResult = false;
+ }
+ if (!bResult){
+ if (pTarget->IdentState == IS_IDNEEDED)
+ pTarget->IdentState = IS_IDFAILED;
+ }
+ else{
+ pTarget->Verified(dwForIP);
+ }
+ return bResult;
+}
+
+bool CClientCreditsList::CryptoAvailable()
+{
+ return (m_nMyPublicKeyLen > 0 && m_pSignkey != 0 && thePrefs.IsSecureIdentEnabled() );
+}
+
+
+#ifdef _DEBUG
+bool CClientCreditsList::Debug_CheckCrypting()
+{
+ // create random key
+ AutoSeededRandomPool rng;
+
+ RSASSA_PKCS1v15_SHA_Signer priv(rng, 384);
+ RSASSA_PKCS1v15_SHA_Verifier pub(priv);
+
+ byte abyPublicKey[80];
+ ArraySink asink(abyPublicKey, 80);
+ pub.DEREncode(asink);
+ uint8 PublicKeyLen = (uint8)asink.TotalPutLength();
+ asink.MessageEnd();
+ uint32 challenge = rand();
+ // create fake client which pretends to be this emule
+ CreditStruct* newcstruct = new CreditStruct;
+ memset(newcstruct, 0, sizeof(CreditStruct));
+ CClientCredits* newcredits = new CClientCredits(newcstruct);
+ newcredits->SetSecureIdent(m_abyMyPublicKey,m_nMyPublicKeyLen);
+ newcredits->m_dwCryptRndChallengeFrom = challenge;
+ // create signature with fake priv key
+ uchar pachSignature[200];
+ memset(pachSignature,0,200);
+ uint8 sigsize = CreateSignature(newcredits,pachSignature,200,0,false, &priv);
+
+
+ // next fake client uses the random created public key
+ CreditStruct* newcstruct2 = new CreditStruct;
+ memset(newcstruct2, 0, sizeof(CreditStruct));
+ CClientCredits* newcredits2 = new CClientCredits(newcstruct2);
+ newcredits2->m_dwCryptRndChallengeFor = challenge;
+
+ // if you uncomment one of the following lines the check has to fail
+ //abyPublicKey[5] = 34;
+ //m_abyMyPublicKey[5] = 22;
+ //pachSignature[5] = 232;
+
+ newcredits2->SetSecureIdent(abyPublicKey,PublicKeyLen);
+
+ //now verify this signature - if it's true everything is fine
+ bool bResult = VerifyIdent(newcredits2,pachSignature,sigsize,0,0);
+
+ delete newcredits;
+ delete newcredits2;
+
+ return bResult;
+}
+#endif
+uint32 CClientCredits::GetSecureWaitStartTime(uint32 dwForIP)
+{
+ if (m_dwUnSecureWaitTime == 0 || m_dwSecureWaitTime == 0)
+ SetSecWaitStartTime(dwForIP);
+
+ if (m_pCredits->nKeySize != 0){ // this client is a SecureHash Client
+ if (GetCurrentIdentState(dwForIP) == IS_IDENTIFIED){ // good boy
+ return m_dwSecureWaitTime;
+ }
+ else{ // not so good boy
+ if (dwForIP == m_dwWaitTimeIP){
+ return m_dwUnSecureWaitTime;
+ }
+ else{ // bad boy
+ // this can also happen if the client has not identified himself yet, but will do later - so maybe he is not a bad boy :) .
+ CString buffer2, buffer;
+ /*for (uint16 i = 0;i != 16;i++){
+ buffer2.Format("%02X",this->m_pCredits->abyKey[i]);
+ buffer+=buffer2;
+ }
+ if (thePrefs.GetLogSecureIdent())
+ AddDebugLogLine(false,"Warning: WaitTime resetted due to Invalid Ident for Userhash %s", buffer);*/
+
+ m_dwUnSecureWaitTime = ::GetTickCount();
+ m_dwWaitTimeIP = dwForIP;
+ return m_dwUnSecureWaitTime;
+ }
+ }
+ }
+ else{ // not a SecureHash Client - handle it like before for now (no security checks)
+ return m_dwUnSecureWaitTime;
+ }
+}
+
+void CClientCredits::SetSecWaitStartTime(uint32 dwForIP)
+{
+ m_dwUnSecureWaitTime = ::GetTickCount()-1;
+ m_dwSecureWaitTime = ::GetTickCount()-1;
+ m_dwWaitTimeIP = dwForIP;
+}
+
+void CClientCredits::ClearWaitStartTime()
+{
+ m_dwUnSecureWaitTime = 0;
+ m_dwSecureWaitTime = 0;
+}
diff --git a/ClientCredits.h b/ClientCredits.h
new file mode 100644
index 00000000..8447e931
--- /dev/null
+++ b/ClientCredits.h
@@ -0,0 +1,134 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "MapKey.h"
+#pragma warning(disable:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+#pragma warning(disable:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4702) // unreachable code
+#include
+#pragma warning(default:4702) // unreachable code
+#pragma warning(default:4100) // unreferenced formal parameter
+#pragma warning(default:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(default:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+
+#define MAXPUBKEYSIZE 80
+
+#define CRYPT_CIP_REMOTECLIENT 10
+#define CRYPT_CIP_LOCALCLIENT 20
+#define CRYPT_CIP_NONECLIENT 30
+
+#pragma pack(1)
+struct CreditStruct_29a{
+ uchar abyKey[16];
+ uint32 nUploadedLo; // uploaded TO him
+ uint32 nDownloadedLo; // downloaded from him
+ uint32 nLastSeen;
+ uint32 nUploadedHi; // upload high 32
+ uint32 nDownloadedHi; // download high 32
+ uint16 nReserved3;
+};
+struct CreditStruct{
+ uchar abyKey[16];
+ uint32 nUploadedLo; // uploaded TO him
+ uint32 nDownloadedLo; // downloaded from him
+ uint32 nLastSeen;
+ uint32 nUploadedHi; // upload high 32
+ uint32 nDownloadedHi; // download high 32
+ uint16 nReserved3;
+ uint8 nKeySize;
+ uchar abySecureIdent[MAXPUBKEYSIZE];
+};
+#pragma pack()
+
+enum EIdentState{
+ IS_NOTAVAILABLE,
+ IS_IDNEEDED,
+ IS_IDENTIFIED,
+ IS_IDFAILED,
+ IS_IDBADGUY,
+};
+
+class CClientCredits
+{
+ friend class CClientCreditsList;
+public:
+ CClientCredits(CreditStruct* in_credits);
+ CClientCredits(const uchar* key);
+ ~CClientCredits();
+
+ const uchar* GetKey() const {return m_pCredits->abyKey;}
+ uchar* GetSecureIdent() {return m_abyPublicKey;}
+ uint8 GetSecIDKeyLen() const {return m_nPublicKeyLen;}
+ CreditStruct* GetDataStruct() const {return m_pCredits;}
+ void ClearWaitStartTime();
+ void AddDownloaded(uint32 bytes, uint32 dwForIP);
+ void AddUploaded(uint32 bytes, uint32 dwForIP);
+ uint64 GetUploadedTotal() const;
+ uint64 GetDownloadedTotal() const;
+ float GetScoreRatio(uint32 dwForIP) const;
+ void SetLastSeen() {m_pCredits->nLastSeen = time(NULL);}
+ bool SetSecureIdent(const uchar* pachIdent, uint8 nIdentLen); // Public key cannot change, use only if there is not public key yet
+ uint32 m_dwCryptRndChallengeFor;
+ uint32 m_dwCryptRndChallengeFrom;
+ EIdentState GetCurrentIdentState(uint32 dwForIP) const; // can be != IdentState
+ uint32 GetSecureWaitStartTime(uint32 dwForIP);
+ void SetSecWaitStartTime(uint32 dwForIP);
+protected:
+ void Verified(uint32 dwForIP);
+ EIdentState IdentState;
+private:
+ void InitalizeIdent();
+ CreditStruct* m_pCredits;
+ byte m_abyPublicKey[80]; // even keys which are not verified will be stored here, and - if verified - copied into the struct
+ uint8 m_nPublicKeyLen;
+ uint32 m_dwIdentIP;
+ uint32 m_dwSecureWaitTime;
+ uint32 m_dwUnSecureWaitTime;
+ uint32 m_dwWaitTimeIP; // client IP assigned to the waittime
+};
+
+class CClientCreditsList
+{
+public:
+ CClientCreditsList();
+ ~CClientCreditsList();
+
+ // return signature size, 0 = Failed | use sigkey param for debug only
+ uint8 CreateSignature(CClientCredits* pTarget, uchar* pachOutput, uint8 nMaxSize, uint32 ChallengeIP, uint8 byChaIPKind, CryptoPP::RSASSA_PKCS1v15_SHA_Signer* sigkey = NULL);
+ bool VerifyIdent(CClientCredits* pTarget, const uchar* pachSignature, uint8 nInputSize, uint32 dwForIP, uint8 byChaIPKind);
+
+ CClientCredits* GetCredit(const uchar* key) ;
+ void Process();
+ uint8 GetPubKeyLen() const {return m_nMyPublicKeyLen;}
+ byte* GetPublicKey() {return m_abyMyPublicKey;}
+ bool CryptoAvailable();
+protected:
+ void LoadList();
+ void SaveList();
+ void InitalizeCrypting();
+ bool CreateKeyPair();
+#ifdef _DEBUG
+ bool Debug_CheckCrypting();
+#endif
+private:
+ CMap m_mapClients;
+ uint32 m_nLastSaved;
+ CryptoPP::RSASSA_PKCS1v15_SHA_Signer* m_pSignkey;
+ byte m_abyMyPublicKey[80];
+ uint8 m_nMyPublicKeyLen;
+};
diff --git a/ClientDetailDialog.cpp b/ClientDetailDialog.cpp
new file mode 100644
index 00000000..8a46a131
--- /dev/null
+++ b/ClientDetailDialog.cpp
@@ -0,0 +1,308 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ClientDetailDialog.h"
+#include "UpDownClient.h"
+#include "PartFile.h"
+#include "ClientCredits.h"
+#include "otherfunctions.h"
+#include "Server.h"
+#include "ServerList.h"
+#include "SharedFileList.h"
+#include "HighColorTab.hpp"
+#include "UserMsgs.h"
+#include "ListenSocket.h"
+#include "preferences.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CClientDetailPage
+
+IMPLEMENT_DYNAMIC(CClientDetailPage, CResizablePage)
+
+BEGIN_MESSAGE_MAP(CClientDetailPage, CResizablePage)
+ ON_MESSAGE(UM_DATA_CHANGED, OnDataChanged)
+END_MESSAGE_MAP()
+
+CClientDetailPage::CClientDetailPage()
+ : CResizablePage(CClientDetailPage::IDD, 0 )
+{
+ m_paClients = NULL;
+ m_bDataChanged = false;
+ m_strCaption = GetResString(IDS_CD_TITLE);
+ m_psp.pszTitle = m_strCaption;
+ m_psp.dwFlags |= PSP_USETITLE;
+}
+
+CClientDetailPage::~CClientDetailPage()
+{
+}
+
+void CClientDetailPage::DoDataExchange(CDataExchange* pDX)
+{
+ CResizablePage::DoDataExchange(pDX);
+}
+
+BOOL CClientDetailPage::OnInitDialog()
+{
+ CResizablePage::OnInitDialog();
+ InitWindowStyles(this);
+
+ AddAnchor(IDC_STATIC30, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_STATIC40, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_STATIC50, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_DNAME, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_DSNAME, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_DDOWNLOADING, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_UPLOADING, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_OBFUSCATION_STAT, TOP_LEFT, TOP_RIGHT);
+
+ Localize();
+ return TRUE;
+}
+
+BOOL CClientDetailPage::OnSetActive()
+{
+ if (!CResizablePage::OnSetActive())
+ return FALSE;
+
+ if (m_bDataChanged)
+ {
+ CUpDownClient* client = STATIC_DOWNCAST(CUpDownClient, (*m_paClients)[0]);
+
+ CString buffer;
+ if (client->GetUserName())
+ GetDlgItem(IDC_DNAME)->SetWindowText(client->GetUserName());
+ else
+ GetDlgItem(IDC_DNAME)->SetWindowText(_T("?"));
+
+ if (client->HasValidHash())
+ GetDlgItem(IDC_DHASH)->SetWindowText(md4str(client->GetUserHash()));
+ else
+ GetDlgItem(IDC_DHASH)->SetWindowText(_T("?"));
+
+ GetDlgItem(IDC_DSOFT)->SetWindowText(client->GetClientSoftVer());
+
+ if (client->SupportsCryptLayer() && thePrefs.IsClientCryptLayerSupported() && (client->RequestsCryptLayer() || thePrefs.IsClientCryptLayerRequested())
+ && (client->IsObfuscatedConnectionEstablished() || !(client->socket != NULL && client->socket->IsConnected())))
+ {
+ buffer = GetResString(IDS_ENABLED);
+ }
+ else if (client->SupportsCryptLayer())
+ buffer = GetResString(IDS_SUPPORTED);
+ else
+ buffer = GetResString(IDS_IDENTNOSUPPORT);
+#if defined(_DEBUG)
+ if (client->IsObfuscatedConnectionEstablished())
+ buffer += _T("(In Use)");
+#endif
+ GetDlgItem(IDC_OBFUSCATION_STAT)->SetWindowText(buffer);
+
+ buffer.Format(_T("%s"),(client->HasLowID() ? GetResString(IDS_IDLOW):GetResString(IDS_IDHIGH)));
+ GetDlgItem(IDC_DID)->SetWindowText(buffer);
+
+ if (client->GetServerIP()){
+ GetDlgItem(IDC_DSIP)->SetWindowText(ipstr(client->GetServerIP()));
+ CServer* cserver = theApp.serverlist->GetServerByIPTCP(client->GetServerIP(), client->GetServerPort());
+ if (cserver)
+ GetDlgItem(IDC_DSNAME)->SetWindowText(cserver->GetListName());
+ else
+ GetDlgItem(IDC_DSNAME)->SetWindowText(_T("?"));
+ }
+ else{
+ GetDlgItem(IDC_DSIP)->SetWindowText(_T("?"));
+ GetDlgItem(IDC_DSNAME)->SetWindowText(_T("?"));
+ }
+
+ CKnownFile* file = theApp.sharedfiles->GetFileByID(client->GetUploadFileID());
+ if (file)
+ GetDlgItem(IDC_DDOWNLOADING)->SetWindowText(file->GetFileName());
+ else
+ GetDlgItem(IDC_DDOWNLOADING)->SetWindowText(_T("-"));
+
+ if (client->GetRequestFile())
+ GetDlgItem(IDC_UPLOADING)->SetWindowText( client->GetRequestFile()->GetFileName() );
+ else
+ GetDlgItem(IDC_UPLOADING)->SetWindowText(_T("-"));
+
+ GetDlgItem(IDC_DDUP)->SetWindowText(CastItoXBytes(client->GetTransferredDown(), false, false));
+
+ GetDlgItem(IDC_DDOWN)->SetWindowText(CastItoXBytes(client->GetTransferredUp(), false, false));
+
+ buffer.Format(_T("%s"), CastItoXBytes(client->GetDownloadDatarate(), false, true));
+ GetDlgItem(IDC_DAVUR)->SetWindowText(buffer);
+
+ buffer.Format(_T("%s"),CastItoXBytes(client->GetDatarate(), false, true));
+ GetDlgItem(IDC_DAVDR)->SetWindowText(buffer);
+
+ if (client->Credits()){
+ GetDlgItem(IDC_DUPTOTAL)->SetWindowText(CastItoXBytes(client->Credits()->GetDownloadedTotal(), false, false));
+ GetDlgItem(IDC_DDOWNTOTAL)->SetWindowText(CastItoXBytes(client->Credits()->GetUploadedTotal(), false, false));
+ buffer.Format(_T("%.1f"),(float)client->Credits()->GetScoreRatio(client->GetIP()));
+ GetDlgItem(IDC_DRATIO)->SetWindowText(buffer);
+
+ if (theApp.clientcredits->CryptoAvailable()){
+ switch(client->Credits()->GetCurrentIdentState(client->GetIP())){
+ case IS_NOTAVAILABLE:
+ GetDlgItem(IDC_CDIDENT)->SetWindowText(GetResString(IDS_IDENTNOSUPPORT));
+ break;
+ case IS_IDFAILED:
+ case IS_IDNEEDED:
+ case IS_IDBADGUY:
+ GetDlgItem(IDC_CDIDENT)->SetWindowText(GetResString(IDS_IDENTFAILED));
+ break;
+ case IS_IDENTIFIED:
+ GetDlgItem(IDC_CDIDENT)->SetWindowText(GetResString(IDS_IDENTOK));
+ break;
+ }
+ }
+ else
+ GetDlgItem(IDC_CDIDENT)->SetWindowText(GetResString(IDS_IDENTNOSUPPORT));
+ }
+ else{
+ GetDlgItem(IDC_DDOWNTOTAL)->SetWindowText(_T("?"));
+ GetDlgItem(IDC_DUPTOTAL)->SetWindowText(_T("?"));
+ GetDlgItem(IDC_DRATIO)->SetWindowText(_T("?"));
+ GetDlgItem(IDC_CDIDENT)->SetWindowText(_T("?"));
+ }
+
+ if (client->GetUserName() && client->Credits()!=NULL){
+ buffer.Format(_T("%.1f"),(float)client->GetScore(false,client->IsDownloading(),true));
+ GetDlgItem(IDC_DRATING)->SetWindowText(buffer);
+ }
+ else
+ GetDlgItem(IDC_DRATING)->SetWindowText(_T("?"));
+
+ if (client->GetUploadState() != US_NONE && client->Credits()!=NULL){
+ if (!client->GetFriendSlot()){
+ buffer.Format(_T("%u"),client->GetScore(false,client->IsDownloading(),false));
+ GetDlgItem(IDC_DSCORE)->SetWindowText(buffer);
+ }
+ else
+ GetDlgItem(IDC_DSCORE)->SetWindowText(GetResString(IDS_FRIENDDETAIL));
+ }
+ else
+ GetDlgItem(IDC_DSCORE)->SetWindowText(_T("-"));
+
+ if (client->GetKadPort() )
+ buffer.Format( _T("%s"), GetResString(IDS_CONNECTED));
+ else
+ buffer.Format( _T("%s"), GetResString(IDS_DISCONNECTED));
+ GetDlgItem(IDC_CLIENTDETAIL_KADCON)->SetWindowText(buffer);
+
+ m_bDataChanged = false;
+ }
+ return TRUE;
+}
+
+LRESULT CClientDetailPage::OnDataChanged(WPARAM, LPARAM)
+{
+ m_bDataChanged = true;
+ return 1;
+}
+
+void CClientDetailPage::Localize()
+{
+ GetDlgItem(IDC_STATIC30)->SetWindowText(GetResString(IDS_CD_GENERAL));
+ GetDlgItem(IDC_STATIC31)->SetWindowText(GetResString(IDS_CD_UNAME));
+ GetDlgItem(IDC_STATIC32)->SetWindowText(GetResString(IDS_CD_UHASH));
+ GetDlgItem(IDC_STATIC33)->SetWindowText(GetResString(IDS_CD_CSOFT) + _T(':'));
+ GetDlgItem(IDC_STATIC35)->SetWindowText(GetResString(IDS_CD_SIP));
+ GetDlgItem(IDC_STATIC38)->SetWindowText(GetResString(IDS_CD_SNAME));
+ GetDlgItem(IDC_STATIC_OBF_LABEL)->SetWindowText(GetResString(IDS_OBFUSCATION) + _T(':'));
+
+ GetDlgItem(IDC_STATIC40)->SetWindowText(GetResString(IDS_CD_TRANS));
+ GetDlgItem(IDC_STATIC41)->SetWindowText(GetResString(IDS_CD_CDOWN));
+ GetDlgItem(IDC_STATIC42)->SetWindowText(GetResString(IDS_CD_DOWN));
+ GetDlgItem(IDC_STATIC43)->SetWindowText(GetResString(IDS_CD_ADOWN));
+ GetDlgItem(IDC_STATIC44)->SetWindowText(GetResString(IDS_CD_TDOWN));
+ GetDlgItem(IDC_STATIC45)->SetWindowText(GetResString(IDS_CD_UP));
+ GetDlgItem(IDC_STATIC46)->SetWindowText(GetResString(IDS_CD_AUP));
+ GetDlgItem(IDC_STATIC47)->SetWindowText(GetResString(IDS_CD_TUP));
+ GetDlgItem(IDC_STATIC48)->SetWindowText(GetResString(IDS_CD_UPLOADREQ));
+
+ GetDlgItem(IDC_STATIC50)->SetWindowText(GetResString(IDS_CD_SCORES));
+ GetDlgItem(IDC_STATIC51)->SetWindowText(GetResString(IDS_CD_MOD));
+ GetDlgItem(IDC_STATIC52)->SetWindowText(GetResString(IDS_CD_RATING));
+ GetDlgItem(IDC_STATIC53)->SetWindowText(GetResString(IDS_CD_USCORE));
+ GetDlgItem(IDC_STATIC133x)->SetWindowText(GetResString(IDS_CD_IDENT));
+ GetDlgItem(IDC_CLIENTDETAIL_KAD)->SetWindowText(GetResString(IDS_KADEMLIA) + _T(":"));
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CClientDetailDialog
+
+IMPLEMENT_DYNAMIC(CClientDetailDialog, CListViewWalkerPropertySheet)
+
+BEGIN_MESSAGE_MAP(CClientDetailDialog, CListViewWalkerPropertySheet)
+ ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+CClientDetailDialog::CClientDetailDialog(CUpDownClient* pClient, CListCtrlItemWalk* pListCtrl)
+ : CListViewWalkerPropertySheet(pListCtrl)
+{
+ m_aItems.Add(pClient);
+ Construct();
+}
+
+CClientDetailDialog::CClientDetailDialog(const CSimpleArray* paClients, CListCtrlItemWalk* pListCtrl)
+ : CListViewWalkerPropertySheet(pListCtrl)
+{
+ for (int i = 0; i < paClients->GetSize(); i++)
+ m_aItems.Add((*paClients)[i]);
+ Construct();
+}
+
+void CClientDetailDialog::Construct()
+{
+ m_psh.dwFlags &= ~PSH_HASHELP;
+ m_psh.dwFlags |= PSH_NOAPPLYNOW;
+
+ m_wndClient.m_psp.dwFlags &= ~PSP_HASHELP;
+ m_wndClient.m_psp.dwFlags |= PSP_USEICONID;
+ m_wndClient.m_psp.pszIcon = _T("CLIENTDETAILS");
+ m_wndClient.SetClients(&m_aItems);
+ AddPage(&m_wndClient);
+}
+
+CClientDetailDialog::~CClientDetailDialog()
+{
+}
+
+void CClientDetailDialog::OnDestroy()
+{
+ CListViewWalkerPropertySheet::OnDestroy();
+}
+
+BOOL CClientDetailDialog::OnInitDialog()
+{
+ EnableStackedTabs(FALSE);
+ BOOL bResult = CListViewWalkerPropertySheet::OnInitDialog();
+ HighColorTab::UpdateImageList(*this);
+ InitWindowStyles(this);
+ EnableSaveRestore(_T("ClientDetailDialog")); // call this after(!) OnInitDialog
+ SetWindowText(GetResString(IDS_CD_TITLE));
+ return bResult;
+}
diff --git a/ClientDetailDialog.h b/ClientDetailDialog.h
new file mode 100644
index 00000000..135ea23c
--- /dev/null
+++ b/ClientDetailDialog.h
@@ -0,0 +1,77 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+
+#include "ResizableLib/ResizablePage.h"
+#include "ResizableLib/ResizableSheet.h"
+#include "ListViewWalkerPropertySheet.h"
+
+class CUpDownClient;
+
+///////////////////////////////////////////////////////////////////////////////
+// CClientDetailPage
+
+class CClientDetailPage : public CResizablePage
+{
+ DECLARE_DYNAMIC(CClientDetailPage)
+
+public:
+ CClientDetailPage(); // standard constructor
+ virtual ~CClientDetailPage();
+
+ void SetClients(const CSimpleArray* paClients) { m_paClients = paClients; m_bDataChanged = true; }
+
+ enum { IDD = IDD_SOURCEDETAILWND };
+
+protected:
+ const CSimpleArray* m_paClients;
+ bool m_bDataChanged;
+
+ void Localize();
+ void RefreshData();
+
+ virtual BOOL OnInitDialog();
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual BOOL OnSetActive();
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg LRESULT OnDataChanged(WPARAM, LPARAM);
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CClientDetailDialog
+
+class CClientDetailDialog : public CListViewWalkerPropertySheet
+{
+ DECLARE_DYNAMIC(CClientDetailDialog)
+
+public:
+ CClientDetailDialog(CUpDownClient* pClient, CListCtrlItemWalk* pListCtrl = NULL);
+ CClientDetailDialog(const CSimpleArray* paClients, CListCtrlItemWalk* pListCtrl = NULL);
+ virtual ~CClientDetailDialog();
+
+protected:
+ CClientDetailPage m_wndClient;
+
+ void Construct();
+
+ virtual BOOL OnInitDialog();
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnDestroy();
+};
diff --git a/ClientList.cpp b/ClientList.cpp
new file mode 100644
index 00000000..94b167f4
--- /dev/null
+++ b/ClientList.cpp
@@ -0,0 +1,1019 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ClientList.h"
+#include "otherfunctions.h"
+#include "Kademlia/Kademlia/kademlia.h"
+#include "Kademlia/Kademlia/prefs.h"
+#include "Kademlia/Kademlia/search.h"
+#include "Kademlia/Kademlia/searchmanager.h"
+#include "Kademlia/routing/contact.h"
+#include "Kademlia/net/kademliaudplistener.h"
+#include "kademlia/kademlia/UDPFirewallTester.h"
+#include "kademlia/utils/UInt128.h"
+#include "LastCommonRouteFinder.h"
+#include "UploadQueue.h"
+#include "DownloadQueue.h"
+#include "UpDownClient.h"
+#include "ClientCredits.h"
+#include "ListenSocket.h"
+#include "Opcodes.h"
+#include "Sockets.h"
+#include "emuledlg.h"
+#include "TransferDlg.h"
+#include "serverwnd.h"
+#include "Log.h"
+#include "packets.h"
+#include "Statistics.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+CClientList::CClientList(){
+ m_dwLastBannCleanUp = 0;
+ m_dwLastTrackedCleanUp = 0;
+ m_dwLastClientCleanUp = 0;
+ m_nBuddyStatus = Disconnected;
+ m_bannedList.InitHashTable(331);
+ m_trackedClientsList.InitHashTable(2011);
+ m_globDeadSourceList.Init(true);
+ m_pBuddy = NULL;
+}
+
+CClientList::~CClientList(){
+ RemoveAllTrackedClients();
+}
+
+void CClientList::GetStatistics(uint32 &ruTotalClients, int stats[NUM_CLIENTLIST_STATS],
+ CMap& clientVersionEDonkey,
+ CMap& clientVersionEDonkeyHybrid,
+ CMap& clientVersionEMule,
+ CMap& clientVersionAMule)
+{
+ ruTotalClients = list.GetCount();
+ memset(stats, 0, sizeof(stats[0]) * NUM_CLIENTLIST_STATS);
+
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL; )
+ {
+ const CUpDownClient* cur_client = list.GetNext(pos);
+
+ if (cur_client->HasLowID())
+ stats[14]++;
+
+ switch (cur_client->GetClientSoft())
+ {
+ case SO_EMULE:
+ case SO_OLDEMULE:
+ stats[2]++;
+ clientVersionEMule[cur_client->GetVersion()]++;
+ break;
+
+ case SO_EDONKEYHYBRID :
+ stats[4]++;
+ clientVersionEDonkeyHybrid[cur_client->GetVersion()]++;
+ break;
+
+ case SO_AMULE:
+ stats[10]++;
+ clientVersionAMule[cur_client->GetVersion()]++;
+ break;
+
+ case SO_EDONKEY:
+ stats[1]++;
+ clientVersionEDonkey[cur_client->GetVersion()]++;
+ break;
+
+ case SO_MLDONKEY:
+ stats[3]++;
+ break;
+
+ case SO_SHAREAZA:
+ stats[11]++;
+ break;
+
+ // all remaining 'eMule Compatible' clients
+ case SO_CDONKEY:
+ case SO_XMULE:
+ case SO_LPHANT:
+ stats[5]++;
+ break;
+
+ default:
+ stats[0]++;
+ break;
+ }
+
+ if (cur_client->Credits() != NULL)
+ {
+ switch (cur_client->Credits()->GetCurrentIdentState(cur_client->GetIP()))
+ {
+ case IS_IDENTIFIED:
+ stats[12]++;
+ break;
+ case IS_IDFAILED:
+ case IS_IDNEEDED:
+ case IS_IDBADGUY:
+ stats[13]++;
+ break;
+ }
+ }
+
+ if (cur_client->GetDownloadState()==DS_ERROR)
+ stats[6]++; // Error
+
+ switch (cur_client->GetUserPort())
+ {
+ case 4662:
+ stats[8]++; // Default Port
+ break;
+ default:
+ stats[9]++; // Other Port
+ }
+
+ // Network client stats
+ if (cur_client->GetServerIP() && cur_client->GetServerPort())
+ {
+ stats[15]++; // eD2K
+ if(cur_client->GetKadPort())
+ {
+ stats[17]++; // eD2K/Kad
+ stats[16]++; // Kad
+ }
+ }
+ else if (cur_client->GetKadPort())
+ stats[16]++; // Kad
+ else
+ stats[18]++; // Unknown
+ }
+}
+
+void CClientList::AddClient(CUpDownClient* toadd, bool bSkipDupTest)
+{
+ // skipping the check for duplicate list entries is only to be done for optimization purposes, if the calling
+ // function has ensured that this client instance is not already within the list -> there are never duplicate
+ // client instances in this list.
+ if (!bSkipDupTest){
+ if(list.Find(toadd))
+ return;
+ }
+ theApp.emuledlg->transferwnd->GetClientList()->AddClient(toadd);
+ list.AddTail(toadd);
+}
+
+// ZZ:UploadSpeedSense -->
+bool CClientList::GiveClientsForTraceRoute() {
+ // this is a host that lastCommonRouteFinder can use to traceroute
+ return theApp.lastCommonRouteFinder->AddHostsToCheck(list);
+}
+// ZZ:UploadSpeedSense <--
+
+void CClientList::RemoveClient(CUpDownClient* toremove, LPCTSTR pszReason){
+ POSITION pos = list.Find(toremove);
+ if (pos){
+ theApp.uploadqueue->RemoveFromUploadQueue(toremove, CString(_T("CClientList::RemoveClient: ")) + pszReason);
+ theApp.uploadqueue->RemoveFromWaitingQueue(toremove);
+ theApp.downloadqueue->RemoveSource(toremove);
+ theApp.emuledlg->transferwnd->GetClientList()->RemoveClient(toremove);
+ list.RemoveAt(pos);
+ }
+ RemoveFromKadList(toremove);
+ RemoveConnectingClient(toremove);
+}
+
+void CClientList::DeleteAll(){
+ theApp.uploadqueue->DeleteAll();
+ theApp.downloadqueue->DeleteAll();
+ POSITION pos1, pos2;
+ for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
+ list.GetNext(pos1);
+ CUpDownClient* cur_client = list.GetAt(pos2);
+ list.RemoveAt(pos2);
+ delete cur_client; // recursiv: this will call RemoveClient
+ }
+}
+
+bool CClientList::AttachToAlreadyKnown(CUpDownClient** client, CClientReqSocket* sender){
+ POSITION pos1, pos2;
+ CUpDownClient* tocheck = (*client);
+ CUpDownClient* found_client = NULL;
+ CUpDownClient* found_client2 = NULL;
+ for (pos1 = list.GetHeadPosition(); (pos2 = pos1) != NULL; ){
+ list.GetNext(pos1);
+ CUpDownClient* cur_client = list.GetAt(pos2);
+ if (tocheck->Compare(cur_client,false)){ //matching userhash
+ found_client2 = cur_client;
+ }
+ if (tocheck->Compare(cur_client,true)){ //matching IP
+ found_client = cur_client;
+ break;
+ }
+ }
+ if (found_client == NULL)
+ found_client = found_client2;
+
+ if (found_client != NULL){
+ if (tocheck == found_client){
+ //we found the same client instance (client may have sent more than one OP_HELLO). do not delete that client!
+ return true;
+ }
+ if (sender){
+ if (found_client->socket){
+ if (found_client->socket->IsConnected()
+ && (found_client->GetIP() != tocheck->GetIP() || found_client->GetUserPort() != tocheck->GetUserPort() ) )
+ {
+ // if found_client is connected and has the IS_IDENTIFIED, it's safe to say that the other one is a bad guy
+ if (found_client->Credits() && found_client->Credits()->GetCurrentIdentState(found_client->GetIP()) == IS_IDENTIFIED){
+ if (thePrefs.GetLogBannedClients())
+ AddDebugLogLine(false, _T("Clients: %s (%s), Banreason: Userhash invalid"), tocheck->GetUserName(), ipstr(tocheck->GetConnectIP()));
+ tocheck->Ban();
+ return false;
+ }
+
+ //IDS_CLIENTCOL Warning: Found matching client, to a currently connected client: %s (%s) and %s (%s)
+ if (thePrefs.GetLogBannedClients())
+ AddDebugLogLine(true,GetResString(IDS_CLIENTCOL), tocheck->GetUserName(), ipstr(tocheck->GetConnectIP()), found_client->GetUserName(), ipstr(found_client->GetConnectIP()));
+ return false;
+ }
+ found_client->socket->client = 0;
+ found_client->socket->Safe_Delete();
+ }
+ found_client->socket = sender;
+ tocheck->socket = 0;
+ }
+ *client = 0;
+ delete tocheck;
+ *client = found_client;
+ return true;
+ }
+ return false;
+}
+
+CUpDownClient* CClientList::FindClientByIP(uint32 clientip, UINT port) const
+{
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;)
+ {
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (cur_client->GetIP() == clientip && cur_client->GetUserPort() == port)
+ return cur_client;
+ }
+ return 0;
+}
+
+CUpDownClient* CClientList::FindClientByUserHash(const uchar* clienthash, uint32 dwIP, uint16 nTCPPort) const
+{
+ CUpDownClient* pFound = NULL;
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;)
+ {
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (!md4cmp(cur_client->GetUserHash() ,clienthash)){
+ if ((dwIP == 0 || dwIP == cur_client->GetIP()) && (nTCPPort == 0 || nTCPPort == cur_client->GetUserPort()))
+ return cur_client;
+ else
+ pFound = pFound != NULL ? pFound : cur_client;
+ }
+ }
+ return pFound;
+}
+
+CUpDownClient* CClientList::FindClientByIP(uint32 clientip) const
+{
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;)
+ {
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (cur_client->GetIP() == clientip)
+ return cur_client;
+ }
+ return 0;
+}
+
+CUpDownClient* CClientList::FindClientByIP_UDP(uint32 clientip, UINT nUDPport) const
+{
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;)
+ {
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (cur_client->GetIP() == clientip && cur_client->GetUDPPort() == nUDPport)
+ return cur_client;
+ }
+ return 0;
+}
+
+CUpDownClient* CClientList::FindClientByUserID_KadPort(uint32 clientID, uint16 kadPort) const
+{
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;)
+ {
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (cur_client->GetUserIDHybrid() == clientID && cur_client->GetKadPort() == kadPort)
+ return cur_client;
+ }
+ return 0;
+}
+
+CUpDownClient* CClientList::FindClientByIP_KadPort(uint32 ip, uint16 port) const
+{
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;)
+ {
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (cur_client->GetIP() == ip && cur_client->GetKadPort() == port)
+ return cur_client;
+ }
+ return 0;
+}
+
+CUpDownClient* CClientList::FindClientByServerID(uint32 uServerIP, uint32 uED2KUserID) const
+{
+ uint32 uHybridUserID = ntohl(uED2KUserID);
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;)
+ {
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (cur_client->GetServerIP() == uServerIP && cur_client->GetUserIDHybrid() == uHybridUserID)
+ return cur_client;
+ }
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Banned clients
+
+void CClientList::AddBannedClient(uint32 dwIP){
+ m_bannedList.SetAt(dwIP, ::GetTickCount());
+}
+
+bool CClientList::IsBannedClient(uint32 dwIP) const
+{
+ uint32 dwBantime;
+ if (m_bannedList.Lookup(dwIP, dwBantime)){
+ if (dwBantime + CLIENTBANTIME > ::GetTickCount())
+ return true;
+ }
+ return false;
+}
+
+void CClientList::RemoveBannedClient(uint32 dwIP){
+ m_bannedList.RemoveKey(dwIP);
+}
+
+void CClientList::RemoveAllBannedClients(){
+ m_bannedList.RemoveAll();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Tracked clients
+
+void CClientList::AddTrackClient(CUpDownClient* toadd){
+ CDeletedClient* pResult = 0;
+ if (m_trackedClientsList.Lookup(toadd->GetIP(), pResult)){
+ pResult->m_dwInserted = ::GetTickCount();
+ for (int i = 0; i != pResult->m_ItemsList.GetCount(); i++){
+ if (pResult->m_ItemsList[i].nPort == toadd->GetUserPort()){
+ // already tracked, update
+ pResult->m_ItemsList[i].pHash = toadd->Credits();
+ return;
+ }
+ }
+ PORTANDHASH porthash = { toadd->GetUserPort(), toadd->Credits()};
+ pResult->m_ItemsList.Add(porthash);
+ }
+ else{
+ m_trackedClientsList.SetAt(toadd->GetIP(), new CDeletedClient(toadd));
+ }
+}
+
+// true = everything ok, hash didn't changed
+// false = hash changed
+bool CClientList::ComparePriorUserhash(uint32 dwIP, uint16 nPort, void* pNewHash){
+ CDeletedClient* pResult = 0;
+ if (m_trackedClientsList.Lookup(dwIP, pResult)){
+ for (int i = 0; i != pResult->m_ItemsList.GetCount(); i++){
+ if (pResult->m_ItemsList[i].nPort == nPort){
+ if (pResult->m_ItemsList[i].pHash != pNewHash)
+ return false;
+ else
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+UINT CClientList::GetClientsFromIP(uint32 dwIP) const
+{
+ CDeletedClient* pResult;
+ if (m_trackedClientsList.Lookup(dwIP, pResult))
+ return pResult->m_ItemsList.GetCount();
+ return 0;
+}
+
+void CClientList::TrackBadRequest(const CUpDownClient* upcClient, int nIncreaseCounter){
+ CDeletedClient* pResult = NULL;
+ if (upcClient->GetIP() == 0){
+ ASSERT( false );
+ return;
+ }
+ if (m_trackedClientsList.Lookup(upcClient->GetIP(), pResult)){
+ pResult->m_dwInserted = ::GetTickCount();
+ pResult->m_cBadRequest += nIncreaseCounter;
+ }
+ else{
+ CDeletedClient* ccToAdd = new CDeletedClient(upcClient);
+ ccToAdd->m_cBadRequest = nIncreaseCounter;
+ m_trackedClientsList.SetAt(upcClient->GetIP(), ccToAdd);
+ }
+}
+
+uint32 CClientList::GetBadRequests(const CUpDownClient* upcClient) const{
+ CDeletedClient* pResult = NULL;
+ if (upcClient->GetIP() == 0){
+ ASSERT( false );
+ return 0;
+ }
+ if (m_trackedClientsList.Lookup(upcClient->GetIP(), pResult)){
+ return pResult->m_cBadRequest;
+ }
+ else
+ return 0;
+}
+
+void CClientList::RemoveAllTrackedClients(){
+ POSITION pos = m_trackedClientsList.GetStartPosition();
+ uint32 nKey;
+ CDeletedClient* pResult;
+ while (pos != NULL){
+ m_trackedClientsList.GetNextAssoc(pos, nKey, pResult);
+ m_trackedClientsList.RemoveKey(nKey);
+ delete pResult;
+ }
+}
+
+void CClientList::Process()
+{
+ ///////////////////////////////////////////////////////////////////////////
+ // Cleanup banned client list
+ //
+ const uint32 cur_tick = ::GetTickCount();
+ if (m_dwLastBannCleanUp + BAN_CLEANUP_TIME < cur_tick)
+ {
+ m_dwLastBannCleanUp = cur_tick;
+
+ POSITION pos = m_bannedList.GetStartPosition();
+ uint32 nKey;
+ uint32 dwBantime;
+ while (pos != NULL)
+ {
+ m_bannedList.GetNextAssoc( pos, nKey, dwBantime );
+ if (dwBantime + CLIENTBANTIME < cur_tick )
+ RemoveBannedClient(nKey);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Cleanup tracked client list
+ //
+ if (m_dwLastTrackedCleanUp + TRACKED_CLEANUP_TIME < cur_tick)
+ {
+ m_dwLastTrackedCleanUp = cur_tick;
+ if (thePrefs.GetLogBannedClients())
+ AddDebugLogLine(false, _T("Cleaning up TrackedClientList, %i clients on List..."), m_trackedClientsList.GetCount());
+ POSITION pos = m_trackedClientsList.GetStartPosition();
+ uint32 nKey;
+ CDeletedClient* pResult;
+ while (pos != NULL)
+ {
+ m_trackedClientsList.GetNextAssoc( pos, nKey, pResult );
+ if (pResult->m_dwInserted + KEEPTRACK_TIME < cur_tick ){
+ m_trackedClientsList.RemoveKey(nKey);
+ delete pResult;
+ }
+ }
+ if (thePrefs.GetLogBannedClients())
+ AddDebugLogLine(false, _T("...done, %i clients left on list"), m_trackedClientsList.GetCount());
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Process Kad client list
+ //
+ //We need to try to connect to the clients in m_KadList
+ //If connected, remove them from the list and send a message back to Kad so we can send a ACK.
+ //If we don't connect, we need to remove the client..
+ //The sockets timeout should delete this object.
+ POSITION pos1, pos2;
+
+ // buddy is just a flag that is used to make sure we are still connected or connecting to a buddy.
+ buddyState buddy = Disconnected;
+
+ for (pos1 = m_KadList.GetHeadPosition(); (pos2 = pos1) != NULL; )
+ {
+ m_KadList.GetNext(pos1);
+ CUpDownClient* cur_client = m_KadList.GetAt(pos2);
+ if( !Kademlia::CKademlia::IsRunning() )
+ {
+ //Clear out this list if we stop running Kad.
+ //Setting the Kad state to KS_NONE causes it to be removed in the switch below.
+ cur_client->SetKadState(KS_NONE);
+ }
+ switch(cur_client->GetKadState())
+ {
+ case KS_QUEUED_FWCHECK:
+ case KS_QUEUED_FWCHECK_UDP:
+ //Another client asked us to try to connect to them to check their firewalled status.
+ cur_client->TryToConnect(true, true);
+ break;
+ case KS_CONNECTING_FWCHECK:
+ //Ignore this state as we are just waiting for results.
+ break;
+ case KS_FWCHECK_UDP:
+ case KS_CONNECTING_FWCHECK_UDP:
+ // we want a UDP firewallcheck from this client and are just waiting to get connected to send the request
+ break;
+ case KS_CONNECTED_FWCHECK:
+ //We successfully connected to the client.
+ //We now send a ack to let them know.
+ if (cur_client->GetKadVersion() >= KADEMLIA_VERSION7_49a){
+ // the result is now sent per TCP instead of UDP, because this will fail if our intern UDP port is unreachable.
+ // But we want the TCP testresult regardless if UDP is firewalled, the new UDP state and test takes care of the rest
+ ASSERT( cur_client->socket != NULL && cur_client->socket->IsConnected() );
+ if (thePrefs.GetDebugClientTCPLevel() > 0)
+ DebugSend("OP_KAD_FWTCPCHECK_ACK", cur_client);
+ Packet* pPacket = new Packet(OP_KAD_FWTCPCHECK_ACK, 0, OP_EMULEPROT);
+ if (!cur_client->SafeConnectAndSendPacket(pPacket))
+ cur_client = NULL;
+ }
+ else {
+ if (thePrefs.GetDebugClientKadUDPLevel() > 0)
+ DebugSend("KADEMLIA_FIREWALLED_ACK_RES", cur_client->GetIP(), cur_client->GetKadPort());
+ Kademlia::CKademlia::GetUDPListener()->SendNullPacket(KADEMLIA_FIREWALLED_ACK_RES, ntohl(cur_client->GetIP()), cur_client->GetKadPort(), 0, NULL);
+ }
+ //We are done with this client. Set Kad status to KS_NONE and it will be removed in the next cycle.
+ if (cur_client != NULL)
+ cur_client->SetKadState(KS_NONE);
+ break;
+
+ case KS_INCOMING_BUDDY:
+ //A firewalled client wants us to be his buddy.
+ //If we already have a buddy, we set Kad state to KS_NONE and it's removed in the next cycle.
+ //If not, this client will change to KS_CONNECTED_BUDDY when it connects.
+ if( m_nBuddyStatus == Connected )
+ cur_client->SetKadState(KS_NONE);
+ break;
+
+ case KS_QUEUED_BUDDY:
+ //We are firewalled and want to request this client to be a buddy.
+ //But first we check to make sure we are not already trying another client.
+ //If we are not already trying. We try to connect to this client.
+ //If we are already connected to a buddy, we set this client to KS_NONE and it's removed next cycle.
+ //If we are trying to connect to a buddy, we just ignore as the one we are trying may fail and we can then try this one.
+ if( m_nBuddyStatus == Disconnected )
+ {
+ buddy = Connecting;
+ m_nBuddyStatus = Connecting;
+ cur_client->SetKadState(KS_CONNECTING_BUDDY);
+ cur_client->TryToConnect(true, true);
+ theApp.emuledlg->serverwnd->UpdateMyInfo();
+ }
+ else if( m_nBuddyStatus == Connected )
+ cur_client->SetKadState(KS_NONE);
+ break;
+
+ case KS_CONNECTING_BUDDY:
+ //We are trying to connect to this client.
+ //Although it should NOT happen, we make sure we are not already connected to a buddy.
+ //If we are we set to KS_NONE and it's removed next cycle.
+ //But if we are not already connected, make sure we set the flag to connecting so we know
+ //things are working correctly.
+ if( m_nBuddyStatus == Connected )
+ cur_client->SetKadState(KS_NONE);
+ else
+ {
+ ASSERT( m_nBuddyStatus == Connecting );
+ buddy = Connecting;
+ }
+ break;
+
+ case KS_CONNECTED_BUDDY:
+ //A potential connected buddy client wanting to me in the Kad network
+ //We set our flag to connected to make sure things are still working correctly.
+ buddy = Connected;
+
+ //If m_nBuddyStatus is not connected already, we set this client as our buddy!
+ if( m_nBuddyStatus != Connected )
+ {
+ m_pBuddy = cur_client;
+ m_nBuddyStatus = Connected;
+ theApp.emuledlg->serverwnd->UpdateMyInfo();
+ }
+ if( m_pBuddy == cur_client && theApp.IsFirewalled() && cur_client->SendBuddyPingPong() )
+ {
+ if (thePrefs.GetDebugClientTCPLevel() > 0)
+ DebugSend("OP__BuddyPing", cur_client);
+ Packet* buddyPing = new Packet(OP_BUDDYPING, 0, OP_EMULEPROT);
+ theStats.AddUpDataOverheadOther(buddyPing->size);
+ VERIFY( cur_client->SendPacket(buddyPing, true, true) );
+ cur_client->SetLastBuddyPingPongTime();
+ }
+ break;
+
+ default:
+ RemoveFromKadList(cur_client);
+ }
+ }
+
+ //We either never had a buddy, or lost our buddy..
+ if( buddy == Disconnected )
+ {
+ if( m_nBuddyStatus != Disconnected || m_pBuddy )
+ {
+ if( Kademlia::CKademlia::IsRunning() && theApp.IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true))
+ {
+ //We are a lowID client and we just lost our buddy.
+ //Go ahead and instantly try to find a new buddy.
+ Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
+ }
+ m_pBuddy = NULL;
+ m_nBuddyStatus = Disconnected;
+ theApp.emuledlg->serverwnd->UpdateMyInfo();
+ }
+ }
+
+ if ( Kademlia::CKademlia::IsConnected() )
+ {
+ //we only need a buddy if direct callback is not available
+ if( Kademlia::CKademlia::IsFirewalled() && Kademlia::CUDPFirewallTester::IsFirewalledUDP(true))
+ {
+ //TODO 0.49b: Kad buddies won'T work with RequireCrypt, so it is disabled for now but should (and will)
+ //be fixed in later version
+ // Update: Buddy connections itself support obfuscation properly since 0.49a (this makes it work fine if our buddy uses require crypt)
+ // ,however callback requests don't support it yet so we wouldn't be able to answer callback requests with RequireCrypt, protocolchange intended for the next version
+ if( m_nBuddyStatus == Disconnected && Kademlia::CKademlia::GetPrefs()->GetFindBuddy() && !thePrefs.IsClientCryptLayerRequired())
+ {
+ DEBUG_ONLY( DebugLog(_T("Starting Buddysearch")) );
+ //We are a firewalled client with no buddy. We have also waited a set time
+ //to try to avoid a false firewalled status.. So lets look for a buddy..
+ if( !Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::FINDBUDDY, true, Kademlia::CUInt128(true).Xor(Kademlia::CKademlia::GetPrefs()->GetKadID())) )
+ {
+ //This search ID was already going. Most likely reason is that
+ //we found and lost our buddy very quickly and the last search hadn't
+ //had time to be removed yet. Go ahead and set this to happen again
+ //next time around.
+ Kademlia::CKademlia::GetPrefs()->SetFindBuddy();
+ }
+ }
+ }
+ else
+ {
+ if( m_pBuddy )
+ {
+ //Lets make sure that if we have a buddy, they are firewalled!
+ //If they are also not firewalled, then someone must have fixed their firewall or stopped saturating their line..
+ //We just set the state of this buddy to KS_NONE and things will be cleared up with the next cycle.
+ if( !m_pBuddy->HasLowID() )
+ m_pBuddy->SetKadState(KS_NONE);
+ }
+ }
+ }
+ else
+ {
+ if( m_pBuddy )
+ {
+ //We are not connected anymore. Just set this buddy to KS_NONE and things will be cleared out on next cycle.
+ m_pBuddy->SetKadState(KS_NONE);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Cleanup client list
+ //
+ CleanUpClientList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Process Direct Callbacks for Timeouts
+ //
+ ProcessConnectingClientsList();
+}
+
+#ifdef _DEBUG
+void CClientList::Debug_SocketDeleted(CClientReqSocket* deleted) const
+{
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL;){
+ CUpDownClient* cur_client = list.GetNext(pos);
+ if (!AfxIsValidAddress(cur_client, sizeof(CUpDownClient))) {
+ AfxDebugBreak();
+ }
+ if (thePrefs.m_iDbgHeap >= 2)
+ ASSERT_VALID(cur_client);
+ if (cur_client->socket == deleted){
+ AfxDebugBreak();
+ }
+ }
+}
+#endif
+
+bool CClientList::IsValidClient(CUpDownClient* tocheck) const
+{
+ if (thePrefs.m_iDbgHeap >= 2)
+ ASSERT_VALID(tocheck);
+ return list.Find(tocheck)!=NULL;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Kad client list
+
+bool CClientList::RequestTCP(Kademlia::CContact* contact, uint8 byConnectOptions)
+{
+ uint32 nContactIP = ntohl(contact->GetIPAddress());
+ // don't connect ourself
+ if (theApp.serverconnect->GetLocalIP() == nContactIP && thePrefs.GetPort() == contact->GetTCPPort())
+ return false;
+
+ CUpDownClient* pNewClient = FindClientByIP(nContactIP, contact->GetTCPPort());
+
+ if (!pNewClient)
+ pNewClient = new CUpDownClient(0, contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, false );
+ else if (pNewClient->GetKadState() != KS_NONE)
+ return false; // already busy with this client in some way (probably buddy stuff), don't mess with it
+
+ //Add client to the lists to be processed.
+ pNewClient->SetKadPort(contact->GetUDPPort());
+ pNewClient->SetKadState(KS_QUEUED_FWCHECK);
+ if (contact->GetClientID() != 0){
+ byte ID[16];
+ contact->GetClientID().ToByteArray(ID);
+ pNewClient->SetUserHash(ID);
+ pNewClient->SetConnectOptions(byConnectOptions, true, false);
+ }
+ m_KadList.AddTail(pNewClient);
+ //This method checks if this is a dup already.
+ AddClient(pNewClient);
+ return true;
+}
+
+void CClientList::RequestBuddy(Kademlia::CContact* contact, uint8 byConnectOptions)
+{
+ uint32 nContactIP = ntohl(contact->GetIPAddress());
+ // don't connect ourself
+ if (theApp.serverconnect->GetLocalIP() == nContactIP && thePrefs.GetPort() == contact->GetTCPPort())
+ return;
+ CUpDownClient* pNewClient = FindClientByIP(nContactIP, contact->GetTCPPort());
+ if (!pNewClient)
+ pNewClient = new CUpDownClient(0, contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, false );
+ else if (pNewClient->GetKadState() != KS_NONE)
+ return; // already busy with this client in some way (probably fw stuff), don't mess with it
+ else if (IsKadFirewallCheckIP(nContactIP)){ // doing a kad firewall check with this IP, abort
+ DEBUG_ONLY( DebugLogWarning(_T("KAD tcp Firewallcheck / Buddy request collosion for IP %s"), ipstr(nContactIP)) );
+ return;
+ }
+ //Add client to the lists to be processed.
+ pNewClient->SetKadPort(contact->GetUDPPort());
+ pNewClient->SetKadState(KS_QUEUED_BUDDY);
+ byte ID[16];
+ contact->GetClientID().ToByteArray(ID);
+ pNewClient->SetUserHash(ID);
+ pNewClient->SetConnectOptions(byConnectOptions, true, false);
+ AddToKadList(pNewClient);
+ //This method checks if this is a dup already.
+ AddClient(pNewClient);
+}
+
+bool CClientList::IncomingBuddy(Kademlia::CContact* contact, Kademlia::CUInt128* buddyID )
+{
+ uint32 nContactIP = ntohl(contact->GetIPAddress());
+ //If eMule already knows this client, abort this.. It could cause conflicts.
+ //Although the odds of this happening is very small, it could still happen.
+ if (FindClientByIP(nContactIP, contact->GetTCPPort()))
+ return false;
+ else if (IsKadFirewallCheckIP(nContactIP)){ // doing a kad firewall check with this IP, abort
+ DEBUG_ONLY( DebugLogWarning(_T("KAD tcp Firewallcheck / Buddy request collosion for IP %s"), ipstr(nContactIP)) );
+ return false;
+ }
+ else if (theApp.serverconnect->GetLocalIP() == nContactIP && thePrefs.GetPort() == contact->GetTCPPort())
+ return false; // don't connect ourself
+
+ //Add client to the lists to be processed.
+ CUpDownClient* pNewClient = new CUpDownClient(0, contact->GetTCPPort(), contact->GetIPAddress(), 0, 0, false );
+ pNewClient->SetKadPort(contact->GetUDPPort());
+ pNewClient->SetKadState(KS_INCOMING_BUDDY);
+ byte ID[16];
+ contact->GetClientID().ToByteArray(ID);
+ pNewClient->SetUserHash(ID); //??
+ buddyID->ToByteArray(ID);
+ pNewClient->SetBuddyID(ID);
+ AddToKadList(pNewClient);
+ AddClient(pNewClient);
+ return true;
+}
+
+void CClientList::RemoveFromKadList(CUpDownClient* torem){
+ POSITION pos = m_KadList.Find(torem);
+ if(pos)
+ {
+ if(torem == m_pBuddy)
+ {
+ m_pBuddy = NULL;
+ theApp.emuledlg->serverwnd->UpdateMyInfo();
+ }
+ m_KadList.RemoveAt(pos);
+ }
+}
+
+void CClientList::AddToKadList(CUpDownClient* toadd){
+ if(!toadd)
+ return;
+ POSITION pos = m_KadList.Find(toadd);
+ if(pos)
+ {
+ return;
+ }
+ m_KadList.AddTail(toadd);
+}
+
+bool CClientList::DoRequestFirewallCheckUDP(const Kademlia::CContact& contact){
+ // first make sure we don't know this IP already from somewhere
+ if (FindClientByIP(ntohl(contact.GetIPAddress())) != NULL)
+ return false;
+ // fine, justcreate the client object, set the state and wait
+ // TODO: We don't know the clients usershash, this means we cannot build an obfuscated connection, which
+ // again mean that the whole check won't work on "Require Obfuscation" setting, which is not a huge problem,
+ // but certainly not nice. Only somewhat acceptable way to solve this is to use the KadID instead.
+ CUpDownClient* pNewClient = new CUpDownClient(0, contact.GetTCPPort(), contact.GetIPAddress(), 0, 0, false );
+ pNewClient->SetKadState(KS_QUEUED_FWCHECK_UDP);
+ DebugLog(_T("Selected client for UDP Firewallcheck: %s"), ipstr(ntohl(contact.GetIPAddress())));
+ AddToKadList(pNewClient);
+ AddClient(pNewClient);
+ ASSERT( !pNewClient->SupportsDirectUDPCallback() );
+ return true;
+}
+
+/*bool CClientList::DebugDoRequestFirewallCheckUDP(uint32 ip, uint16 port){
+ // first make sure we don't know this IP already from somewhere
+ // fine, justcreate the client object, set the state and wait
+ // TODO: We don't know the clients usershash, this means we cannot build an obfuscated connection, which
+ // again mean that the whole check won't work on "Require Obfuscation" setting, which is not a huge problem,
+ // but certainly not nice. Only somewhat acceptable way to solve this is to use the KadID instead.
+ CUpDownClient* pNewClient = new CUpDownClient(0, port, ip, 0, 0, false );
+ pNewClient->SetKadState(KS_QUEUED_FWCHECK_UDP);
+ DebugLog(_T("Selected client for UDP Firewallcheck: %s"), ipstr(ntohl(ip)));
+ AddToKadList(pNewClient);
+ AddClient(pNewClient);
+ ASSERT( !pNewClient->SupportsDirectUDPCallback() );
+ return true;
+}*/
+
+
+
+void CClientList::CleanUpClientList(){
+ // we remove clients which are not needed any more by time
+ // this check is also done on CUpDownClient::Disconnected, however it will not catch all
+ // cases (if a client changes the state without beeing connected
+ //
+ // Adding this check directly to every point where any state changes would be more effective,
+ // is however not compatible with the current code, because there are points where a client has
+ // no state for some code lines and the code is also not prepared that a client object gets
+ // invalid while working with it (aka setting a new state)
+ // so this way is just the easy and safe one to go (as long as emule is basically single threaded)
+ const uint32 cur_tick = ::GetTickCount();
+ if (m_dwLastClientCleanUp + CLIENTLIST_CLEANUP_TIME < cur_tick ){
+ m_dwLastClientCleanUp = cur_tick;
+ POSITION pos1, pos2;
+ uint32 cDeleted = 0;
+ for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
+ list.GetNext(pos1);
+ CUpDownClient* pCurClient = list.GetAt(pos2);
+ if ((pCurClient->GetUploadState() == US_NONE || pCurClient->GetUploadState() == US_BANNED && !pCurClient->IsBanned())
+ && pCurClient->GetDownloadState() == DS_NONE
+ && pCurClient->GetChatState() == MS_NONE
+ && pCurClient->GetKadState() == KS_NONE
+ && pCurClient->socket == NULL)
+ {
+ cDeleted++;
+ delete pCurClient;
+ }
+ }
+ DEBUG_ONLY(AddDebugLogLine(false,_T("Cleaned ClientList, removed %i not used known clients"), cDeleted));
+ }
+}
+
+
+CDeletedClient::CDeletedClient(const CUpDownClient* pClient)
+{
+ m_cBadRequest = 0;
+ m_dwInserted = ::GetTickCount();
+ PORTANDHASH porthash = { pClient->GetUserPort(), pClient->Credits()};
+ m_ItemsList.Add(porthash);
+}
+
+// ZZ:DownloadManager -->
+void CClientList::ProcessA4AFClients() const {
+ //if(thePrefs.GetLogA4AF()) AddDebugLogLine(false, _T(">>> Starting A4AF check"));
+ POSITION pos1, pos2;
+ for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
+ list.GetNext(pos1);
+ CUpDownClient* cur_client = list.GetAt(pos2);
+
+ if(cur_client->GetDownloadState() != DS_DOWNLOADING &&
+ cur_client->GetDownloadState() != DS_CONNECTED &&
+ (!cur_client->m_OtherRequests_list.IsEmpty() || !cur_client->m_OtherNoNeeded_list.IsEmpty())) {
+ //AddDebugLogLine(false, _T("+++ ZZ:DownloadManager: Trying for better file for source: %s '%s'"), cur_client->GetUserName(), cur_client->reqfile->GetFileName());
+ cur_client->SwapToAnotherFile(_T("Periodic A4AF check CClientList::ProcessA4AFClients()"), false, false, false, NULL, true, false);
+ }
+ }
+ //if(thePrefs.GetLogA4AF()) AddDebugLogLine(false, _T(">>> Done with A4AF check"));
+}
+// <-- ZZ:DownloadManager
+
+void CClientList::AddKadFirewallRequest(uint32 dwIP){
+ IPANDTICS add = {dwIP, ::GetTickCount()};
+ listFirewallCheckRequests.AddHead(add);
+ while (!listFirewallCheckRequests.IsEmpty()){
+ if (::GetTickCount() - listFirewallCheckRequests.GetTail().dwInserted > SEC2MS(180))
+ listFirewallCheckRequests.RemoveTail();
+ else
+ break;
+ }
+}
+
+bool CClientList::IsKadFirewallCheckIP(uint32 dwIP) const{
+ for (POSITION pos = listFirewallCheckRequests.GetHeadPosition(); pos != NULL; listFirewallCheckRequests.GetNext(pos)){
+ if (listFirewallCheckRequests.GetAt(pos).dwIP == dwIP && ::GetTickCount() - listFirewallCheckRequests.GetAt(pos).dwInserted < SEC2MS(180))
+ return true;
+ }
+ return false;
+}
+
+void CClientList::AddConnectingClient(CUpDownClient* pToAdd){
+ for (POSITION pos = m_liConnectingClients.GetHeadPosition(); pos != NULL; m_liConnectingClients.GetNext(pos)){
+ if (m_liConnectingClients.GetAt(pos).pClient == pToAdd){
+ ASSERT( false );
+ return;
+ }
+ }
+ ASSERT( pToAdd->GetConnectingState() != CCS_NONE );
+ CONNECTINGCLIENT cc = {pToAdd, ::GetTickCount()};
+ m_liConnectingClients.AddTail(cc);
+}
+
+void CClientList::ProcessConnectingClientsList(){
+ // we do check if any connects have timed out by now
+ const uint32 cur_tick = ::GetTickCount();
+ POSITION pos1, pos2;
+ for (pos1 = m_liConnectingClients.GetHeadPosition();( pos2 = pos1 ) != NULL;){
+ m_liConnectingClients.GetNext(pos1);
+ CONNECTINGCLIENT cc = m_liConnectingClients.GetAt(pos2);
+ if (cc.dwInserted + SEC2MS(45) < cur_tick)
+ {
+ ASSERT( cc.pClient->GetConnectingState() != CCS_NONE );
+ m_liConnectingClients.RemoveAt(pos2);
+ CString dbgInfo;
+ if (cc.pClient->Disconnected(_T("Connectiontry Timeout")))
+ delete cc.pClient;
+ }
+ }
+}
+
+void CClientList::RemoveConnectingClient(CUpDownClient* pToRemove){
+ for (POSITION pos = m_liConnectingClients.GetHeadPosition(); pos != NULL; m_liConnectingClients.GetNext(pos)){
+ if (m_liConnectingClients.GetAt(pos).pClient == pToRemove){
+ m_liConnectingClients.RemoveAt(pos);
+ return;
+ }
+ }
+}
+
+void CClientList::AddTrackCallbackRequests(uint32 dwIP){
+ IPANDTICS add = {dwIP, ::GetTickCount()};
+ listDirectCallbackRequests.AddHead(add);
+ while (!listDirectCallbackRequests.IsEmpty()){
+ if (::GetTickCount() - listDirectCallbackRequests.GetTail().dwInserted > MIN2MS(3))
+ listDirectCallbackRequests.RemoveTail();
+ else
+ break;
+ }
+}
+
+bool CClientList::AllowCalbackRequest(uint32 dwIP) const
+{
+ for (POSITION pos = listDirectCallbackRequests.GetHeadPosition(); pos != NULL; listDirectCallbackRequests.GetNext(pos)){
+ if (listDirectCallbackRequests.GetAt(pos).dwIP == dwIP && ::GetTickCount() - listDirectCallbackRequests.GetAt(pos).dwInserted < MIN2MS(3))
+ return false;
+ }
+ return true;
+}
diff --git a/ClientList.h b/ClientList.h
new file mode 100644
index 00000000..efa8cc78
--- /dev/null
+++ b/ClientList.h
@@ -0,0 +1,159 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "DeadSourceList.h"
+
+class CClientReqSocket;
+class CUpDownClient;
+namespace Kademlia{
+ class CContact;
+ class CUInt128;
+};
+typedef CTypedPtrList CUpDownClientPtrList;
+
+#define NUM_CLIENTLIST_STATS 19
+#define BAN_CLEANUP_TIME 1200000 // 20 min
+
+//------------CDeletedClient Class----------------------
+// this class / list is a bit overkill, but currently needed to avoid any exploit possibtility
+// it will keep track of certain clients attributes for 2 hours, while the CUpDownClient object might be deleted already
+// currently: IP, Port, UserHash
+struct PORTANDHASH{
+ uint16 nPort;
+ void* pHash;
+};
+
+struct IPANDTICS{
+ uint32 dwIP;
+ uint32 dwInserted;
+};
+struct CONNECTINGCLIENT{
+ CUpDownClient* pClient;
+ uint32 dwInserted;
+};
+
+
+class CDeletedClient{
+public:
+ CDeletedClient(const CUpDownClient* pClient);
+ CArray m_ItemsList;
+ uint32 m_dwInserted;
+ uint32 m_cBadRequest;
+};
+
+enum buddyState
+{
+ Disconnected,
+ Connecting,
+ Connected
+};
+
+// ----------------------CClientList Class---------------
+class CClientList
+{
+ friend class CClientListCtrl;
+
+public:
+ CClientList();
+ ~CClientList();
+
+ // Clients
+ void AddClient(CUpDownClient* toadd,bool bSkipDupTest = false);
+ void RemoveClient(CUpDownClient* toremove, LPCTSTR pszReason = NULL);
+ void GetStatistics(uint32& totalclient, int stats[NUM_CLIENTLIST_STATS],
+ CMap& clientVersionEDonkey,
+ CMap& clientVersionEDonkeyHybrid,
+ CMap& clientVersionEMule,
+ CMap& clientVersionAMule);
+ uint32 GetClientCount() { return list.GetCount();}
+ void DeleteAll();
+ bool AttachToAlreadyKnown(CUpDownClient** client, CClientReqSocket* sender);
+ CUpDownClient* FindClientByIP(uint32 clientip, UINT port) const;
+ CUpDownClient* FindClientByUserHash(const uchar* clienthash, uint32 dwIP = 0, uint16 nTCPPort = 0) const;
+ CUpDownClient* FindClientByIP(uint32 clientip) const;
+ CUpDownClient* FindClientByIP_UDP(uint32 clientip, UINT nUDPport) const;
+ CUpDownClient* FindClientByServerID(uint32 uServerIP, uint32 uUserID) const;
+ CUpDownClient* FindClientByUserID_KadPort(uint32 clientID,uint16 kadPort) const;
+ CUpDownClient* FindClientByIP_KadPort(uint32 ip, uint16 port) const;
+
+ // Banned clients
+ void AddBannedClient(uint32 dwIP);
+ bool IsBannedClient(uint32 dwIP) const;
+ void RemoveBannedClient(uint32 dwIP);
+ UINT GetBannedCount() const { return m_bannedList.GetCount(); }
+ void RemoveAllBannedClients();
+
+ // Tracked clients
+ void AddTrackClient(CUpDownClient* toadd);
+ bool ComparePriorUserhash(uint32 dwIP, uint16 nPort, void* pNewHash);
+ UINT GetClientsFromIP(uint32 dwIP) const;
+ void TrackBadRequest(const CUpDownClient* upcClient, int nIncreaseCounter);
+ uint32 GetBadRequests(const CUpDownClient* upcClient) const;
+ UINT GetTrackedCount() const { return m_trackedClientsList.GetCount(); }
+ void RemoveAllTrackedClients();
+
+ // Kad client list, buddy handling
+ bool RequestTCP(Kademlia::CContact* contact, uint8 byConnectOptions);
+ void RequestBuddy(Kademlia::CContact* contact, uint8 byConnectOptions);
+ bool IncomingBuddy(Kademlia::CContact* contact, Kademlia::CUInt128* buddyID);
+ void RemoveFromKadList(CUpDownClient* torem);
+ void AddToKadList(CUpDownClient* toadd);
+ bool DoRequestFirewallCheckUDP(const Kademlia::CContact& contact);
+ //bool DebugDoRequestFirewallCheckUDP(uint32 ip, uint16 port);
+ uint8 GetBuddyStatus() { return m_nBuddyStatus; }
+ CUpDownClient* GetBuddy() { return m_pBuddy; }
+
+ void AddKadFirewallRequest(uint32 dwIP);
+ bool IsKadFirewallCheckIP(uint32 dwIP) const;
+
+ // Direct Callback List
+ void AddTrackCallbackRequests(uint32 dwIP);
+ bool AllowCalbackRequest(uint32 dwIP) const;
+
+ // Connecting Clients
+ void AddConnectingClient(CUpDownClient* pToAdd);
+ void RemoveConnectingClient(CUpDownClient* pToRemove);
+
+ void Process();
+ bool IsValidClient(CUpDownClient* tocheck) const;
+ void Debug_SocketDeleted(CClientReqSocket* deleted) const;
+
+ // ZZ:UploadSpeedSense -->
+ bool GiveClientsForTraceRoute();
+ // ZZ:UploadSpeedSense <--
+
+ void ProcessA4AFClients() const; // ZZ:DownloadManager
+ CDeadSourceList m_globDeadSourceList;
+
+protected:
+ void CleanUpClientList();
+ void ProcessConnectingClientsList();
+
+private:
+ CUpDownClientPtrList list;
+ CUpDownClientPtrList m_KadList;
+ CMap m_bannedList;
+ CMap m_trackedClientsList;
+ uint32 m_dwLastBannCleanUp;
+ uint32 m_dwLastTrackedCleanUp;
+ uint32 m_dwLastClientCleanUp;
+ CUpDownClient* m_pBuddy;
+ uint8 m_nBuddyStatus;
+ CList listFirewallCheckRequests;
+ CList listDirectCallbackRequests;
+ CList m_liConnectingClients;
+};
diff --git a/ClientListCtrl.cpp b/ClientListCtrl.cpp
new file mode 100644
index 00000000..9130b0db
--- /dev/null
+++ b/ClientListCtrl.cpp
@@ -0,0 +1,581 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ClientListCtrl.h"
+#include "otherfunctions.h"
+#include "MenuCmds.h"
+#include "ClientDetailDialog.h"
+#include "KademliaWnd.h"
+#include "ClientList.h"
+#include "emuledlg.h"
+#include "FriendList.h"
+#include "TransferDlg.h"
+#include "MemDC.h"
+#include "UpDownClient.h"
+#include "ClientCredits.h"
+#include "ListenSocket.h"
+#include "ChatWnd.h"
+#include "Kademlia/Kademlia/Kademlia.h"
+#include "Kademlia/net/KademliaUDPListener.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+IMPLEMENT_DYNAMIC(CClientListCtrl, CMuleListCtrl)
+
+BEGIN_MESSAGE_MAP(CClientListCtrl, CMuleListCtrl)
+ ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnClick)
+ ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetDispInfo)
+ ON_NOTIFY_REFLECT(NM_DBLCLK, OnNmDblClk)
+ ON_WM_CONTEXTMENU()
+ ON_WM_SYSCOLORCHANGE()
+END_MESSAGE_MAP()
+
+CClientListCtrl::CClientListCtrl()
+ : CListCtrlItemWalk(this)
+{
+ SetGeneralPurposeFind(true);
+ SetSkinKey(L"ClientsLv");
+}
+
+void CClientListCtrl::Init()
+{
+ SetPrefsKey(_T("ClientListCtrl"));
+ SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+
+ InsertColumn(0, GetResString(IDS_QL_USERNAME), LVCFMT_LEFT, DFLT_CLIENTNAME_COL_WIDTH);
+ InsertColumn(1, GetResString(IDS_CL_UPLOADSTATUS), LVCFMT_LEFT, 100);
+ InsertColumn(2, GetResString(IDS_CL_TRANSFUP), LVCFMT_RIGHT, DFLT_SIZE_COL_WIDTH);
+ InsertColumn(3, GetResString(IDS_CL_DOWNLSTATUS), LVCFMT_LEFT, 100);
+ InsertColumn(4, GetResString(IDS_CL_TRANSFDOWN), LVCFMT_RIGHT, DFLT_SIZE_COL_WIDTH);
+ InsertColumn(5, GetResString(IDS_CD_CSOFT), LVCFMT_LEFT, DFLT_CLIENTSOFT_COL_WIDTH);
+ InsertColumn(6, GetResString(IDS_CONNECTED), LVCFMT_LEFT, 50);
+ CString coltemp;
+ coltemp = GetResString(IDS_CD_UHASH);
+ coltemp.Remove(_T(':'));
+ InsertColumn(7, coltemp, LVCFMT_LEFT, DFLT_HASH_COL_WIDTH);
+
+ SetAllIcons();
+ Localize();
+ LoadSettings();
+ SetSortArrow();
+ SortItems(SortProc, GetSortItem() + (GetSortAscending() ? 0 : 100));
+}
+
+void CClientListCtrl::Localize()
+{
+ CHeaderCtrl *pHeaderCtrl = GetHeaderCtrl();
+ HDITEM hdi;
+ hdi.mask = HDI_TEXT;
+
+ CString strRes;
+ strRes = GetResString(IDS_QL_USERNAME);
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(0, &hdi);
+
+ strRes = GetResString(IDS_CL_UPLOADSTATUS);
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(1, &hdi);
+
+ strRes = GetResString(IDS_CL_TRANSFUP);
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(2, &hdi);
+
+ strRes = GetResString(IDS_CL_DOWNLSTATUS);
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(3, &hdi);
+
+ strRes = GetResString(IDS_CL_TRANSFDOWN);
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(4, &hdi);
+
+ strRes = GetResString(IDS_CD_CSOFT);
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(5, &hdi);
+
+ strRes = GetResString(IDS_CONNECTED);
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(6, &hdi);
+
+ strRes = GetResString(IDS_CD_UHASH);
+ strRes.Remove(_T(':'));
+ hdi.pszText = const_cast((LPCTSTR)strRes);
+ pHeaderCtrl->SetItem(7, &hdi);
+}
+
+void CClientListCtrl::OnSysColorChange()
+{
+ CMuleListCtrl::OnSysColorChange();
+ SetAllIcons();
+}
+
+void CClientListCtrl::SetAllIcons()
+{
+ ApplyImageList(NULL);
+ m_ImageList.DeleteImageList();
+ m_ImageList.Create(16, 16, theApp.m_iDfltImageListColorFlags | ILC_MASK, 0, 1);
+ m_ImageList.Add(CTempIconLoader(_T("ClientEDonkey")));
+ m_ImageList.Add(CTempIconLoader(_T("ClientCompatible")));
+ m_ImageList.Add(CTempIconLoader(_T("Friend")));
+ m_ImageList.Add(CTempIconLoader(_T("ClientMLDonkey")));
+ m_ImageList.Add(CTempIconLoader(_T("ClientEDonkeyHybrid")));
+ m_ImageList.Add(CTempIconLoader(_T("ClientShareaza")));
+ m_ImageList.Add(CTempIconLoader(_T("Server")));
+ m_ImageList.Add(CTempIconLoader(_T("ClientAMule")));
+ m_ImageList.Add(CTempIconLoader(_T("ClientLPhant")));
+ m_ImageList.SetOverlayImage(m_ImageList.Add(CTempIconLoader(_T("ClientSecureOvl"))), 1);
+ m_ImageList.SetOverlayImage(m_ImageList.Add(CTempIconLoader(_T("OverlayObfu"))), 2);
+ m_ImageList.SetOverlayImage(m_ImageList.Add(CTempIconLoader(_T("OverlaySecureObfu"))), 3);
+ // Apply the image list also to the listview control, even if we use our own 'DrawItem'.
+ // This is needed to give the listview control a chance to initialize the row height.
+ ASSERT( (GetStyle() & LVS_SHAREIMAGELISTS) != 0 );
+ VERIFY( ApplyImageList(m_ImageList) == NULL );
+}
+
+void CClientListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
+{
+ if (!theApp.emuledlg->IsRunning())
+ return;
+ if (!lpDrawItemStruct->itemData)
+ return;
+
+ CMemDC dc(CDC::FromHandle(lpDrawItemStruct->hDC), &lpDrawItemStruct->rcItem);
+ BOOL bCtrlFocused;
+ InitItemMemDC(dc, lpDrawItemStruct, bCtrlFocused);
+ CRect cur_rec(lpDrawItemStruct->rcItem);
+ CRect rcClient;
+ GetClientRect(&rcClient);
+ const CUpDownClient *client = (CUpDownClient *)lpDrawItemStruct->itemData;
+
+ CHeaderCtrl *pHeaderCtrl = GetHeaderCtrl();
+ int iCount = pHeaderCtrl->GetItemCount();
+ cur_rec.right = cur_rec.left - sm_iLabelOffset;
+ cur_rec.left += sm_iIconOffset;
+ for (int iCurrent = 0; iCurrent < iCount; iCurrent++)
+ {
+ int iColumn = pHeaderCtrl->OrderToIndex(iCurrent);
+ if (!IsColumnHidden(iColumn))
+ {
+ UINT uDrawTextAlignment;
+ int iColumnWidth = GetColumnWidth(iColumn, uDrawTextAlignment);
+ cur_rec.right += iColumnWidth;
+ if (cur_rec.left < cur_rec.right && HaveIntersection(rcClient, cur_rec))
+ {
+ TCHAR szItem[1024];
+ GetItemDisplayText(client, iColumn, szItem, _countof(szItem));
+ switch (iColumn)
+ {
+ case 0:{
+ int iImage;
+ if (client->IsFriend())
+ iImage = 2;
+ else if (client->GetClientSoft() == SO_EDONKEYHYBRID)
+ iImage = 4;
+ else if (client->GetClientSoft() == SO_MLDONKEY)
+ iImage = 3;
+ else if (client->GetClientSoft() == SO_SHAREAZA)
+ iImage = 5;
+ else if (client->GetClientSoft() == SO_URL)
+ iImage = 6;
+ else if (client->GetClientSoft() == SO_AMULE)
+ iImage = 7;
+ else if (client->GetClientSoft() == SO_LPHANT)
+ iImage = 8;
+ else if (client->ExtProtocolAvailable())
+ iImage = 1;
+ else
+ iImage = 0;
+
+ UINT nOverlayImage = 0;
+ if ((client->Credits() && client->Credits()->GetCurrentIdentState(client->GetIP()) == IS_IDENTIFIED))
+ nOverlayImage |= 1;
+ if (client->IsObfuscatedConnectionEstablished())
+ nOverlayImage |= 2;
+ int iIconPosY = (cur_rec.Height() > 16) ? ((cur_rec.Height() - 16) / 2) : 1;
+ POINT point = { cur_rec.left, cur_rec.top + iIconPosY };
+ m_ImageList.Draw(dc, iImage, point, ILD_NORMAL | INDEXTOOVERLAYMASK(nOverlayImage));
+
+ cur_rec.left += 16 + sm_iLabelOffset;
+ dc.DrawText(szItem, -1, &cur_rec, MLC_DT_TEXT | uDrawTextAlignment);
+ cur_rec.left -= 16;
+ cur_rec.right -= sm_iSubItemInset;
+ break;
+ }
+
+ default:
+ dc.DrawText(szItem, -1, &cur_rec, MLC_DT_TEXT | uDrawTextAlignment);
+ break;
+ }
+ }
+ cur_rec.left += iColumnWidth;
+ }
+ }
+
+ DrawFocusRect(dc, lpDrawItemStruct->rcItem, lpDrawItemStruct->itemState & ODS_FOCUS, bCtrlFocused, lpDrawItemStruct->itemState & ODS_SELECTED);
+}
+
+void CClientListCtrl::GetItemDisplayText(const CUpDownClient *client, int iSubItem, LPTSTR pszText, int cchTextMax)
+{
+ if (pszText == NULL || cchTextMax <= 0) {
+ ASSERT(0);
+ return;
+ }
+ pszText[0] = _T('\0');
+ switch (iSubItem)
+ {
+ case 0:
+ if (client->GetUserName() == NULL)
+ _sntprintf(pszText, cchTextMax, _T("(%s)"), GetResString(IDS_UNKNOWN));
+ else
+ _tcsncpy(pszText, client->GetUserName(), cchTextMax);
+ break;
+
+ case 1:
+ _tcsncpy(pszText, client->GetUploadStateDisplayString(), cchTextMax);
+ break;
+
+ case 2:
+ _tcsncpy(pszText, client->credits != NULL ? CastItoXBytes(client->credits->GetUploadedTotal(), false, false) : _T(""), cchTextMax);
+ break;
+
+ case 3:
+ _tcsncpy(pszText, client->GetDownloadStateDisplayString(), cchTextMax);
+ break;
+
+ case 4:
+ _tcsncpy(pszText, client->credits != NULL ? CastItoXBytes(client->credits->GetDownloadedTotal(), false, false) : _T(""), cchTextMax);
+ break;
+
+ case 5:
+ _tcsncpy(pszText, client->GetClientSoftVer(), cchTextMax);
+ if (pszText[0] == _T('\0'))
+ _tcsncpy(pszText, GetResString(IDS_UNKNOWN), cchTextMax);
+ break;
+
+ case 6:
+ _tcsncpy(pszText, GetResString((client->socket && client->socket->IsConnected()) ? IDS_YES : IDS_NO), cchTextMax);
+ break;
+
+ case 7:
+ _tcsncpy(pszText, md4str(client->GetUserHash()), cchTextMax);
+ break;
+ }
+ pszText[cchTextMax - 1] = _T('\0');
+}
+
+void CClientListCtrl::OnLvnGetDispInfo(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ if (theApp.emuledlg->IsRunning()) {
+ // Although we have an owner drawn listview control we store the text for the primary item in the listview, to be
+ // capable of quick searching those items via the keyboard. Because our listview items may change their contents,
+ // we do this via a text callback function. The listview control will send us the LVN_DISPINFO notification if
+ // it needs to know the contents of the primary item.
+ //
+ // But, the listview control sends this notification all the time, even if we do not search for an item. At least
+ // this notification is only sent for the visible items and not for all items in the list. Though, because this
+ // function is invoked *very* often, do *NOT* put any time consuming code in here.
+ //
+ // Vista: That callback is used to get the strings for the label tips for the sub(!) items.
+ //
+ NMLVDISPINFO *pDispInfo = reinterpret_cast(pNMHDR);
+ if (pDispInfo->item.mask & LVIF_TEXT) {
+ const CUpDownClient* pClient = reinterpret_cast(pDispInfo->item.lParam);
+ if (pClient != NULL)
+ GetItemDisplayText(pClient, pDispInfo->item.iSubItem, pDispInfo->item.pszText, pDispInfo->item.cchTextMax);
+ }
+ }
+ *pResult = 0;
+}
+
+void CClientListCtrl::OnLvnColumnClick(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ NMLISTVIEW *pNMListView = (NMLISTVIEW *)pNMHDR;
+ bool sortAscending;
+ if (GetSortItem() != pNMListView->iSubItem)
+ {
+ switch (pNMListView->iSubItem)
+ {
+ case 1: // Upload State
+ case 2: // Uploaded Total
+ case 4: // Downloaded Total
+ case 5: // Client Software
+ case 6: // Connected
+ sortAscending = false;
+ break;
+ default:
+ sortAscending = true;
+ break;
+ }
+ }
+ else
+ sortAscending = !GetSortAscending();
+
+ // Sort table
+ UpdateSortHistory(pNMListView->iSubItem + (sortAscending ? 0 : 100));
+ SetSortArrow(pNMListView->iSubItem, sortAscending);
+ SortItems(SortProc, pNMListView->iSubItem + (sortAscending ? 0 : 100));
+
+ *pResult = 0;
+}
+
+int CClientListCtrl::SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ const CUpDownClient *item1 = (CUpDownClient *)lParam1;
+ const CUpDownClient *item2 = (CUpDownClient *)lParam2;
+ int iColumn = (lParamSort >= 100) ? lParamSort - 100 : lParamSort;
+ int iResult = 0;
+ switch (iColumn)
+ {
+ case 0:
+ if (item1->GetUserName() && item2->GetUserName())
+ iResult = CompareLocaleStringNoCase(item1->GetUserName(), item2->GetUserName());
+ else if (item1->GetUserName() == NULL)
+ iResult = 1; // place clients with no usernames at bottom
+ else if (item2->GetUserName() == NULL)
+ iResult = -1; // place clients with no usernames at bottom
+ break;
+
+ case 1:
+ iResult = item1->GetUploadState() - item2->GetUploadState();
+ break;
+
+ case 2:
+ if (item1->credits && item2->credits)
+ iResult = CompareUnsigned64(item1->credits->GetUploadedTotal(), item2->credits->GetUploadedTotal());
+ else if (item1->credits)
+ iResult = 1;
+ else
+ iResult = -1;
+ break;
+
+ case 3:
+ if (item1->GetDownloadState() == item2->GetDownloadState())
+ {
+ if (item1->IsRemoteQueueFull() && item2->IsRemoteQueueFull())
+ iResult = 0;
+ else if (item1->IsRemoteQueueFull())
+ iResult = 1;
+ else if (item2->IsRemoteQueueFull())
+ iResult = -1;
+ }
+ else
+ iResult = item1->GetDownloadState() - item2->GetDownloadState();
+ break;
+
+ case 4:
+ if (item1->credits && item2->credits)
+ iResult = CompareUnsigned64(item1->credits->GetDownloadedTotal(), item2->credits->GetDownloadedTotal());
+ else if (item1->credits)
+ iResult = 1;
+ else
+ iResult = -1;
+ break;
+
+ case 5:
+ if (item1->GetClientSoft() == item2->GetClientSoft())
+ iResult = item1->GetVersion() - item2->GetVersion();
+ else
+ iResult = -(item1->GetClientSoft() - item2->GetClientSoft()); // invert result to place eMule's at top
+ break;
+
+ case 6:
+ if (item1->socket && item2->socket)
+ iResult = item1->socket->IsConnected() - item2->socket->IsConnected();
+ else if (item1->socket)
+ iResult = 1;
+ else
+ iResult = -1;
+ break;
+
+ case 7:
+ iResult = memcmp(item1->GetUserHash(), item2->GetUserHash(), 16);
+ break;
+ }
+
+ if (lParamSort >= 100)
+ iResult = -iResult;
+
+ //call secondary sortorder, if this one results in equal
+ int dwNextSort;
+ if (iResult == 0 && (dwNextSort = theApp.emuledlg->transferwnd->GetClientList()->GetNextSortOrder(lParamSort)) != -1)
+ iResult = SortProc(lParam1, lParam2, dwNextSort);
+
+ return iResult;
+}
+
+void CClientListCtrl::OnNmDblClk(NMHDR* /*pNMHDR*/, LRESULT* pResult)
+{
+ int iSel = GetNextItem(-1, LVIS_SELECTED | LVIS_FOCUSED);
+ if (iSel != -1) {
+ CUpDownClient* client = (CUpDownClient*)GetItemData(iSel);
+ if (client){
+ CClientDetailDialog dialog(client, this);
+ dialog.DoModal();
+ }
+ }
+ *pResult = 0;
+}
+
+void CClientListCtrl::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
+{
+ int iSel = GetNextItem(-1, LVIS_SELECTED | LVIS_FOCUSED);
+ const CUpDownClient* client = (iSel != -1) ? (CUpDownClient*)GetItemData(iSel) : NULL;
+
+ CTitleMenu ClientMenu;
+ ClientMenu.CreatePopupMenu();
+ ClientMenu.AddMenuTitle(GetResString(IDS_CLIENTS), true);
+ ClientMenu.AppendMenu(MF_STRING | (client ? MF_ENABLED : MF_GRAYED), MP_DETAIL, GetResString(IDS_SHOWDETAILS), _T("CLIENTDETAILS"));
+ ClientMenu.SetDefaultItem(MP_DETAIL);
+ ClientMenu.AppendMenu(MF_STRING | ((client && client->IsEd2kClient() && !client->IsFriend()) ? MF_ENABLED : MF_GRAYED), MP_ADDFRIEND, GetResString(IDS_ADDFRIEND), _T("ADDFRIEND"));
+ ClientMenu.AppendMenu(MF_STRING | ((client && client->IsEd2kClient()) ? MF_ENABLED : MF_GRAYED), MP_MESSAGE, GetResString(IDS_SEND_MSG), _T("SENDMESSAGE"));
+ ClientMenu.AppendMenu(MF_STRING | ((client && client->IsEd2kClient() && client->GetViewSharedFilesSupport()) ? MF_ENABLED : MF_GRAYED), MP_SHOWLIST, GetResString(IDS_VIEWFILES), _T("VIEWFILES"));
+ if (Kademlia::CKademlia::IsRunning() && !Kademlia::CKademlia::IsConnected())
+ ClientMenu.AppendMenu(MF_STRING | ((client && client->IsEd2kClient() && client->GetKadPort()!=0 && client->GetKadVersion() > 1) ? MF_ENABLED : MF_GRAYED), MP_BOOT, GetResString(IDS_BOOTSTRAP));
+ ClientMenu.AppendMenu(MF_STRING | (GetItemCount() > 0 ? MF_ENABLED : MF_GRAYED), MP_FIND, GetResString(IDS_FIND), _T("Search"));
+ GetPopupMenuPos(*this, point);
+ ClientMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
+}
+
+BOOL CClientListCtrl::OnCommand(WPARAM wParam, LPARAM /*lParam*/)
+{
+ wParam = LOWORD(wParam);
+
+ switch (wParam)
+ {
+ case MP_FIND:
+ OnFindStart();
+ return TRUE;
+ }
+
+ int iSel = GetNextItem(-1, LVIS_SELECTED | LVIS_FOCUSED);
+ if (iSel != -1){
+ CUpDownClient* client = (CUpDownClient*)GetItemData(iSel);
+ switch (wParam){
+ case MP_SHOWLIST:
+ client->RequestSharedFileList();
+ break;
+ case MP_MESSAGE:
+ theApp.emuledlg->chatwnd->StartSession(client);
+ break;
+ case MP_ADDFRIEND:
+ if (theApp.friendlist->AddFriend(client))
+ Update(iSel);
+ break;
+ case MP_UNBAN:
+ if (client->IsBanned()){
+ client->UnBan();
+ Update(iSel);
+ }
+ break;
+ case MP_DETAIL:
+ case MPG_ALTENTER:
+ case IDA_ENTER:
+ {
+ CClientDetailDialog dialog(client, this);
+ dialog.DoModal();
+ break;
+ }
+ case MP_BOOT:
+ if (client->GetKadPort() && client->GetKadVersion() > 1)
+ Kademlia::CKademlia::Bootstrap(ntohl(client->GetIP()), client->GetKadPort());
+ break;
+ }
+ }
+ return true;
+}
+
+void CClientListCtrl::AddClient(const CUpDownClient *client)
+{
+ if (!theApp.emuledlg->IsRunning())
+ return;
+ if (thePrefs.IsKnownClientListDisabled())
+ return;
+
+ int iItemCount = GetItemCount();
+ InsertItem(LVIF_TEXT | LVIF_PARAM, iItemCount, LPSTR_TEXTCALLBACK, 0, 0, 0, (LPARAM)client);
+ theApp.emuledlg->transferwnd->UpdateListCount(CTransferDlg::wnd2Clients, iItemCount + 1);
+}
+
+void CClientListCtrl::RemoveClient(const CUpDownClient *client)
+{
+ if (!theApp.emuledlg->IsRunning())
+ return;
+
+ LVFINDINFO find;
+ find.flags = LVFI_PARAM;
+ find.lParam = (LPARAM)client;
+ int result = FindItem(&find);
+ if (result != -1) {
+ DeleteItem(result);
+ theApp.emuledlg->transferwnd->UpdateListCount(CTransferDlg::wnd2Clients);
+ }
+}
+
+void CClientListCtrl::RefreshClient(const CUpDownClient *client)
+{
+ if (!theApp.emuledlg->IsRunning())
+ return;
+
+ if (theApp.emuledlg->activewnd != theApp.emuledlg->transferwnd || !theApp.emuledlg->transferwnd->GetClientList()->IsWindowVisible())
+ return;
+
+ LVFINDINFO find;
+ find.flags = LVFI_PARAM;
+ find.lParam = (LPARAM)client;
+ int result = FindItem(&find);
+ if (result != -1)
+ Update(result);
+}
+
+void CClientListCtrl::ShowSelectedUserDetails()
+{
+ POINT point;
+ ::GetCursorPos(&point);
+ CPoint p = point;
+ ScreenToClient(&p);
+ int it = HitTest(p);
+ if (it == -1)
+ return;
+
+ SetItemState(-1, 0, LVIS_SELECTED);
+ SetItemState(it, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+ SetSelectionMark(it); // display selection mark correctly!
+
+ CUpDownClient* client = (CUpDownClient*)GetItemData(GetSelectionMark());
+ if (client){
+ CClientDetailDialog dialog(client, this);
+ dialog.DoModal();
+ }
+}
+
+void CClientListCtrl::ShowKnownClients()
+{
+ DeleteAllItems();
+ int iItemCount = 0;
+ for (POSITION pos = theApp.clientlist->list.GetHeadPosition(); pos != NULL; ) {
+ const CUpDownClient *cur_client = theApp.clientlist->list.GetNext(pos);
+ int iItem = InsertItem(LVIF_TEXT | LVIF_PARAM, iItemCount, LPSTR_TEXTCALLBACK, 0, 0, 0, (LPARAM)cur_client);
+ Update(iItem);
+ iItemCount++;
+ }
+ theApp.emuledlg->transferwnd->UpdateListCount(CTransferDlg::wnd2Clients, iItemCount);
+}
diff --git a/ClientListCtrl.h b/ClientListCtrl.h
new file mode 100644
index 00000000..9d67ec2e
--- /dev/null
+++ b/ClientListCtrl.h
@@ -0,0 +1,56 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "MuleListCtrl.h"
+#include "ListCtrlItemWalk.h"
+
+class CUpDownClient;
+
+class CClientListCtrl : public CMuleListCtrl, public CListCtrlItemWalk
+{
+ DECLARE_DYNAMIC(CClientListCtrl)
+
+public:
+ CClientListCtrl();
+
+ void Init();
+ void AddClient(const CUpDownClient *client);
+ void RemoveClient(const CUpDownClient *client);
+ void RefreshClient(const CUpDownClient *client);
+ void Hide() { ShowWindow(SW_HIDE); }
+ void Show() { ShowWindow(SW_SHOW); }
+ void Localize();
+ void ShowSelectedUserDetails();
+ void ShowKnownClients();
+
+protected:
+ CImageList m_ImageList;
+
+ void SetAllIcons();
+ void GetItemDisplayText(const CUpDownClient *pClient, int iSubItem, LPTSTR pszText, int cchTextMax);
+ static int CALLBACK SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+ virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnLvnColumnClick(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnLvnGetDispInfo(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
+ afx_msg void OnNmDblClk(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnSysColorChange();
+};
diff --git a/ClientStateDefs.h b/ClientStateDefs.h
new file mode 100644
index 00000000..b87d3d83
--- /dev/null
+++ b/ClientStateDefs.h
@@ -0,0 +1,156 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+
+enum EUploadState{
+ US_UPLOADING,
+ US_ONUPLOADQUEUE,
+ US_CONNECTING,
+ US_BANNED,
+ US_NONE
+};
+
+enum EDownloadState{
+ DS_DOWNLOADING,
+ DS_ONQUEUE,
+ DS_CONNECTED,
+ DS_CONNECTING,
+ DS_WAITCALLBACK,
+ DS_WAITCALLBACKKAD,
+ DS_REQHASHSET,
+ DS_NONEEDEDPARTS,
+ DS_TOOMANYCONNS,
+ DS_TOOMANYCONNSKAD,
+ DS_LOWTOLOWIP,
+ DS_BANNED,
+ DS_ERROR,
+ DS_NONE,
+ DS_REMOTEQUEUEFULL // not used yet, except in statistics
+};
+
+enum EPeerCacheDownState{
+ PCDS_NONE = 0,
+ PCDS_WAIT_CLIENT_REPLY,
+ PCDS_WAIT_CACHE_REPLY,
+ PCDS_DOWNLOADING
+};
+
+enum EPeerCacheUpState{
+ PCUS_NONE = 0,
+ PCUS_WAIT_CACHE_REPLY,
+ PCUS_UPLOADING
+};
+
+enum EChatState{
+ MS_NONE,
+ MS_CHATTING,
+ MS_CONNECTING,
+ MS_UNABLETOCONNECT
+};
+
+enum EKadState{
+ KS_NONE,
+ KS_QUEUED_FWCHECK,
+ KS_CONNECTING_FWCHECK,
+ KS_CONNECTED_FWCHECK,
+ KS_QUEUED_BUDDY,
+ KS_INCOMING_BUDDY,
+ KS_CONNECTING_BUDDY,
+ KS_CONNECTED_BUDDY,
+ KS_QUEUED_FWCHECK_UDP,
+ KS_FWCHECK_UDP,
+ KS_CONNECTING_FWCHECK_UDP
+};
+
+enum EClientSoftware{
+ SO_EMULE = 0, // default
+ SO_CDONKEY = 1, // ET_COMPATIBLECLIENT
+ SO_XMULE = 2, // ET_COMPATIBLECLIENT
+ SO_AMULE = 3, // ET_COMPATIBLECLIENT
+ SO_SHAREAZA = 4, // ET_COMPATIBLECLIENT
+ SO_MLDONKEY = 10, // ET_COMPATIBLECLIENT
+ SO_LPHANT = 20, // ET_COMPATIBLECLIENT
+ // other client types which are not identified with ET_COMPATIBLECLIENT
+ SO_EDONKEYHYBRID = 50,
+ SO_EDONKEY,
+ SO_OLDEMULE,
+ SO_URL,
+ SO_UNKNOWN
+};
+
+enum ESecureIdentState{
+ IS_UNAVAILABLE = 0,
+ IS_ALLREQUESTSSEND = 0,
+ IS_SIGNATURENEEDED = 1,
+ IS_KEYANDSIGNEEDED = 2,
+};
+
+enum EInfoPacketState{
+ IP_NONE = 0,
+ IP_EDONKEYPROTPACK = 1,
+ IP_EMULEPROTPACK = 2,
+ IP_BOTH = 3,
+};
+
+enum ESourceFrom{
+ SF_SERVER = 0,
+ SF_KADEMLIA = 1,
+ SF_SOURCE_EXCHANGE = 2,
+ SF_PASSIVE = 3,
+ SF_LINK = 4
+};
+
+enum EChatCaptchaState{
+ CA_NONE = 0,
+ CA_CHALLENGESENT,
+ CA_CAPTCHASOLVED,
+ CA_ACCEPTING,
+ CA_CAPTCHARECV,
+ CA_SOLUTIONSENT
+};
+
+enum EConnectingState{
+ CCS_NONE = 0,
+ CCS_DIRECTTCP,
+ CCS_DIRECTCALLBACK,
+ CCS_KADCALLBACK,
+ CCS_SERVERCALLBACK,
+ CCS_PRECONDITIONS
+};
+
+#ifdef _DEBUG
+ // use the 'Enums' only for debug builds, each enum costs 4 bytes (3 unused)
+#define _EClientSoftware EClientSoftware
+#define _EChatState EChatState
+#define _EKadState EKadState
+#define _ESecureIdentState ESecureIdentState
+#define _EUploadState EUploadState
+#define _EDownloadState EDownloadState
+#define _ESourceFrom ESourceFrom
+#define _EChatCaptchaState EChatCaptchaState
+#define _EConnectingState EConnectingState
+#else
+#define _EClientSoftware uint8
+#define _EChatState uint8
+#define _EKadState uint8
+#define _ESecureIdentState uint8
+#define _EUploadState uint8
+#define _EDownloadState uint8
+#define _ESourceFrom uint8
+#define _EChatCaptchaState uint8
+#define _EConnectingState uint8
+#endif
\ No newline at end of file
diff --git a/ClientUDPSocket.cpp b/ClientUDPSocket.cpp
new file mode 100644
index 00000000..51effa82
--- /dev/null
+++ b/ClientUDPSocket.cpp
@@ -0,0 +1,624 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ClientUDPSocket.h"
+#include "Packets.h"
+#include "DownloadQueue.h"
+#include "Statistics.h"
+#include "PartFile.h"
+#include "SharedFileList.h"
+#include "UploadQueue.h"
+#include "UpDownClient.h"
+#include "Preferences.h"
+#include "OtherFunctions.h"
+#include "SafeFile.h"
+#include "ClientList.h"
+#include "Listensocket.h"
+#include
+#include "kademlia/kademlia/Kademlia.h"
+#include "kademlia/kademlia/UDPFirewallTester.h"
+#include "kademlia/net/KademliaUDPListener.h"
+#include "kademlia/io/IOException.h"
+#include "IPFilter.h"
+#include "Log.h"
+#include "EncryptedDatagramSocket.h"
+#include "./kademlia/kademlia/prefs.h"
+#include "./kademlia/utils/KadUDPKey.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+// CClientUDPSocket
+
+CClientUDPSocket::CClientUDPSocket()
+{
+ m_bWouldBlock = false;
+ m_port=0;
+}
+
+CClientUDPSocket::~CClientUDPSocket()
+{
+ theApp.uploadBandwidthThrottler->RemoveFromAllQueues(this); // ZZ:UploadBandWithThrottler (UDP)
+
+ POSITION pos = controlpacket_queue.GetHeadPosition();
+ while (pos){
+ UDPPack* p = controlpacket_queue.GetNext(pos);
+ delete p->packet;
+ delete p;
+ }
+}
+
+void CClientUDPSocket::OnReceive(int nErrorCode)
+{
+ if (nErrorCode)
+ {
+ if (thePrefs.GetVerbose())
+ DebugLogError(_T("Error: Client UDP socket, error on receive event: %s"), GetErrorMessage(nErrorCode, 1));
+ }
+
+ BYTE buffer[5000];
+ SOCKADDR_IN sockAddr = {0};
+ int iSockAddrLen = sizeof sockAddr;
+ int nRealLen = ReceiveFrom(buffer, sizeof buffer, (SOCKADDR*)&sockAddr, &iSockAddrLen);
+ if (!(theApp.ipfilter->IsFiltered(sockAddr.sin_addr.S_un.S_addr) || theApp.clientlist->IsBannedClient(sockAddr.sin_addr.S_un.S_addr)))
+ {
+ BYTE* pBuffer;
+ uint32 nReceiverVerifyKey;
+ uint32 nSenderVerifyKey;
+ int nPacketLen = DecryptReceivedClient(buffer, nRealLen, &pBuffer, sockAddr.sin_addr.S_un.S_addr, &nReceiverVerifyKey, &nSenderVerifyKey);
+ if (nPacketLen >= 1)
+ {
+ CString strError;
+ try
+ {
+ switch (pBuffer[0])
+ {
+ case OP_EMULEPROT:
+ {
+ if (nPacketLen >= 2)
+ ProcessPacket(pBuffer+2, nPacketLen-2, pBuffer[1], sockAddr.sin_addr.S_un.S_addr, ntohs(sockAddr.sin_port));
+ else
+ throw CString(_T("eMule packet too short"));
+ break;
+ }
+ case OP_KADEMLIAPACKEDPROT:
+ {
+ theStats.AddDownDataOverheadKad(nPacketLen);
+ if (nPacketLen >= 2)
+ {
+ uint32 nNewSize = nPacketLen*10+300;
+ BYTE* unpack = NULL;
+ uLongf unpackedsize = 0;
+ int iZLibResult = 0;
+ do {
+ delete[] unpack;
+ unpack = new BYTE[nNewSize];
+ unpackedsize = nNewSize-2;
+ iZLibResult = uncompress(unpack+2, &unpackedsize, pBuffer+2, nPacketLen-2);
+ nNewSize *= 2; // size for the next try if needed
+ } while (iZLibResult == Z_BUF_ERROR && nNewSize < 250000);
+
+ if (iZLibResult == Z_OK)
+ {
+ unpack[0] = OP_KADEMLIAHEADER;
+ unpack[1] = pBuffer[1];
+ try
+ {
+ Kademlia::CKademlia::ProcessPacket(unpack, unpackedsize+2, ntohl(sockAddr.sin_addr.S_un.S_addr), ntohs(sockAddr.sin_port)
+ , (Kademlia::CPrefs::GetUDPVerifyKey(sockAddr.sin_addr.S_un.S_addr) == nReceiverVerifyKey)
+ , Kademlia::CKadUDPKey(nSenderVerifyKey, theApp.GetPublicIP(false)) );
+ }
+ catch(...)
+ {
+ delete[] unpack;
+ throw;
+ }
+ }
+ else
+ {
+ delete[] unpack;
+ CString strError;
+ strError.Format(_T("Failed to uncompress Kad packet: zip error: %d (%hs)"), iZLibResult, zError(iZLibResult));
+ throw strError;
+ }
+ delete[] unpack;
+ }
+ else
+ throw CString(_T("Kad packet (compressed) too short"));
+ break;
+ }
+ case OP_KADEMLIAHEADER:
+ {
+ theStats.AddDownDataOverheadKad(nPacketLen);
+ if (nPacketLen >= 2)
+ Kademlia::CKademlia::ProcessPacket(pBuffer, nPacketLen, ntohl(sockAddr.sin_addr.S_un.S_addr), ntohs(sockAddr.sin_port)
+ , (Kademlia::CPrefs::GetUDPVerifyKey(sockAddr.sin_addr.S_un.S_addr) == nReceiverVerifyKey)
+ , Kademlia::CKadUDPKey(nSenderVerifyKey, theApp.GetPublicIP(false)) );
+ else
+ throw CString(_T("Kad packet too short"));
+ break;
+ }
+ default:
+ {
+ CString strError;
+ strError.Format(_T("Unknown protocol 0x%02x"), pBuffer[0]);
+ throw strError;
+ }
+ }
+ }
+ catch(CFileException* error)
+ {
+ error->Delete();
+ strError = _T("Invalid packet received");
+ }
+ catch(CMemoryException* error)
+ {
+ error->Delete();
+ strError = _T("Memory exception");
+ }
+ catch(CString error)
+ {
+ strError = error;
+ }
+ catch(Kademlia::CIOException* error)
+ {
+ error->Delete();
+ strError = _T("Invalid packet received");
+ }
+ catch(CException* error)
+ {
+ error->Delete();
+ strError = _T("General packet error");
+ }
+ #ifndef _DEBUG
+ catch(...)
+ {
+ strError = _T("Unknown exception");
+ ASSERT(0);
+ }
+ #endif
+ if (thePrefs.GetVerbose() && !strError.IsEmpty())
+ {
+ CString strClientInfo;
+ CUpDownClient* client;
+ if (pBuffer[0] == OP_EMULEPROT)
+ client = theApp.clientlist->FindClientByIP_UDP(sockAddr.sin_addr.S_un.S_addr, ntohs(sockAddr.sin_port));
+ else
+ client = theApp.clientlist->FindClientByIP_KadPort(sockAddr.sin_addr.S_un.S_addr, ntohs(sockAddr.sin_port));
+ if (client)
+ strClientInfo = client->DbgGetClientInfo();
+ else
+ strClientInfo.Format(_T("%s:%u"), ipstr(sockAddr.sin_addr), ntohs(sockAddr.sin_port));
+
+ DebugLogWarning(_T("Client UDP socket: prot=0x%02x opcode=0x%02x sizeaftercrypt=%u realsize=%u %s: %s"), pBuffer[0], pBuffer[1], nPacketLen, nRealLen, strError, strClientInfo);
+ }
+ }
+ else if (nPacketLen == SOCKET_ERROR)
+ {
+ DWORD dwError = WSAGetLastError();
+ if (dwError == WSAECONNRESET)
+ {
+ // Depending on local and remote OS and depending on used local (remote?) router we may receive
+ // WSAECONNRESET errors. According some KB articles, this is a special way of winsock to report
+ // that a sent UDP packet was not received by the remote host because it was not listening on
+ // the specified port -> no eMule running there.
+ //
+ // TODO: So, actually we should do something with this information and drop the related Kad node
+ // or eMule client...
+ ;
+ }
+ if (thePrefs.GetVerbose() && dwError != WSAECONNRESET)
+ {
+ CString strClientInfo;
+ if (iSockAddrLen > 0 && sockAddr.sin_addr.S_un.S_addr != 0 && sockAddr.sin_addr.S_un.S_addr != INADDR_NONE)
+ strClientInfo.Format(_T(" from %s:%u"), ipstr(sockAddr.sin_addr), ntohs(sockAddr.sin_port));
+ DebugLogError(_T("Error: Client UDP socket, failed to receive data%s: %s"), strClientInfo, GetErrorMessage(dwError, 1));
+ }
+ }
+ }
+}
+
+bool CClientUDPSocket::ProcessPacket(const BYTE* packet, UINT size, uint8 opcode, uint32 ip, uint16 port)
+{
+ switch(opcode)
+ {
+ case OP_REASKCALLBACKUDP:
+ {
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_ReaskCallbackUDP", NULL, NULL, ip);
+ theStats.AddDownDataOverheadOther(size);
+ CUpDownClient* buddy = theApp.clientlist->GetBuddy();
+ if( buddy )
+ {
+ if( size < 17 || buddy->socket == NULL )
+ break;
+ if (!md4cmp(packet, buddy->GetBuddyID()))
+ {
+ PokeUInt32(const_cast(packet)+10, ip);
+ PokeUInt16(const_cast(packet)+14, port);
+ Packet* response = new Packet(OP_EMULEPROT);
+ response->opcode = OP_REASKCALLBACKTCP;
+ response->pBuffer = new char[size];
+ memcpy(response->pBuffer, packet+10, size-10);
+ response->size = size-10;
+ if (thePrefs.GetDebugClientTCPLevel() > 0)
+ DebugSend("OP__ReaskCallbackTCP", buddy);
+ theStats.AddUpDataOverheadFileRequest(response->size);
+ buddy->SendPacket(response, true);
+ }
+ }
+ break;
+ }
+ case OP_REASKFILEPING:
+ {
+ theStats.AddDownDataOverheadFileRequest(size);
+ CSafeMemFile data_in(packet, size);
+ uchar reqfilehash[16];
+ data_in.ReadHash16(reqfilehash);
+ CKnownFile* reqfile = theApp.sharedfiles->GetFileByID(reqfilehash);
+
+ bool bSenderMultipleIpUnknown = false;
+ CUpDownClient* sender = theApp.uploadqueue->GetWaitingClientByIP_UDP(ip, port, true, &bSenderMultipleIpUnknown);
+ if (!reqfile)
+ {
+ if (thePrefs.GetDebugClientUDPLevel() > 0) {
+ DebugRecv("OP_ReaskFilePing", NULL, reqfilehash, ip);
+ DebugSend("OP__FileNotFound", NULL);
+ }
+
+ Packet* response = new Packet(OP_FILENOTFOUND,0,OP_EMULEPROT);
+ theStats.AddUpDataOverheadFileRequest(response->size);
+ if (sender != NULL)
+ SendPacket(response, ip, port, sender->ShouldReceiveCryptUDPPackets(), sender->GetUserHash(), false, 0);
+ else
+ SendPacket(response, ip, port, false, NULL, false, 0);
+ break;
+ }
+ if (sender)
+ {
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_ReaskFilePing", sender, reqfilehash);
+
+ //Make sure we are still thinking about the same file
+ if (md4cmp(reqfilehash, sender->GetUploadFileID()) == 0)
+ {
+ sender->AddAskedCount();
+ sender->SetLastUpRequest();
+ //I messed up when I first added extended info to UDP
+ //I should have originally used the entire ProcessExtenedInfo the first time.
+ //So now I am forced to check UDPVersion to see if we are sending all the extended info.
+ //For now on, we should not have to change anything here if we change
+ //anything to the extended info data as this will be taken care of in ProcessExtendedInfo()
+ //Update extended info.
+ if (sender->GetUDPVersion() > 3)
+ {
+ sender->ProcessExtendedInfo(&data_in, reqfile);
+ }
+ //Update our complete source counts.
+ else if (sender->GetUDPVersion() > 2)
+ {
+ uint16 nCompleteCountLast= sender->GetUpCompleteSourcesCount();
+ uint16 nCompleteCountNew = data_in.ReadUInt16();
+ sender->SetUpCompleteSourcesCount(nCompleteCountNew);
+ if (nCompleteCountLast != nCompleteCountNew)
+ {
+ reqfile->UpdatePartsInfo();
+ }
+ }
+ CSafeMemFile data_out(128);
+ if(sender->GetUDPVersion() > 3)
+ {
+ if (reqfile->IsPartFile())
+ ((CPartFile*)reqfile)->WritePartStatus(&data_out);
+ else
+ data_out.WriteUInt16(0);
+ }
+ data_out.WriteUInt16((uint16)(theApp.uploadqueue->GetWaitingPosition(sender)));
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugSend("OP__ReaskAck", sender);
+ Packet* response = new Packet(&data_out, OP_EMULEPROT);
+ response->opcode = OP_REASKACK;
+ theStats.AddUpDataOverheadFileRequest(response->size);
+ SendPacket(response, ip, port, sender->ShouldReceiveCryptUDPPackets(), sender->GetUserHash(), false, 0);
+ }
+ else
+ {
+ DebugLogError(_T("Client UDP socket; ReaskFilePing; reqfile does not match"));
+ TRACE(_T("reqfile: %s\n"), DbgGetFileInfo(reqfile->GetFileHash()));
+ TRACE(_T("sender->GetRequestFile(): %s\n"), sender->GetRequestFile() ? DbgGetFileInfo(sender->GetRequestFile()->GetFileHash()) : _T("(null)"));
+ }
+ }
+ else
+ {
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_ReaskFilePing", NULL, reqfilehash, ip);
+ // Don't answer him. We probably have him on our queue already, but can't locate him. Force him to establish a TCP connection
+ if (!bSenderMultipleIpUnknown){
+ if (((uint32)theApp.uploadqueue->GetWaitingUserCount() + 50) > thePrefs.GetQueueSize())
+ {
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugSend("OP__QueueFull", NULL);
+ Packet* response = new Packet(OP_QUEUEFULL,0,OP_EMULEPROT);
+ theStats.AddUpDataOverheadFileRequest(response->size);
+ SendPacket(response, ip, port, false, NULL, false, 0); // we cannot answer this one encrypted since we dont know this client
+ }
+ }
+ else{
+ DebugLogWarning(_T("UDP Packet received - multiple clients with the same IP but different UDP port found. Possible UDP Portmapping problem, enforcing TCP connection. IP: %s, Port: %u"), ipstr(ip), port);
+ }
+ }
+ break;
+ }
+ case OP_QUEUEFULL:
+ {
+ theStats.AddDownDataOverheadFileRequest(size);
+ CUpDownClient* sender = theApp.downloadqueue->GetDownloadClientByIP_UDP(ip, port, true);
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_QueueFull", sender, NULL, ip);
+ if (sender && sender->UDPPacketPending()){
+ sender->SetRemoteQueueFull(true);
+ sender->UDPReaskACK(0);
+ }
+ else if (sender != NULL)
+ DebugLogError(_T("Received UDP Packet (OP_QUEUEFULL) which was not requested (pendingflag == false); Ignored packet - %s"), sender->DbgGetClientInfo());
+ break;
+ }
+ case OP_REASKACK:
+ {
+ theStats.AddDownDataOverheadFileRequest(size);
+ CUpDownClient* sender = theApp.downloadqueue->GetDownloadClientByIP_UDP(ip, port, true);
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_ReaskAck", sender, NULL, ip);
+ if (sender && sender->UDPPacketPending()){
+ CSafeMemFile data_in(packet, size);
+ if ( sender->GetUDPVersion() > 3 )
+ {
+ sender->ProcessFileStatus(true, &data_in, sender->GetRequestFile());
+ }
+ uint16 nRank = data_in.ReadUInt16();
+ sender->SetRemoteQueueFull(false);
+ sender->UDPReaskACK(nRank);
+ sender->AddAskedCountDown();
+ }
+ else if (sender != NULL)
+ DebugLogError(_T("Received UDP Packet (OP_REASKACK) which was not requested (pendingflag == false); Ignored packet - %s"), sender->DbgGetClientInfo());
+
+ break;
+ }
+ case OP_FILENOTFOUND:
+ {
+ theStats.AddDownDataOverheadFileRequest(size);
+ CUpDownClient* sender = theApp.downloadqueue->GetDownloadClientByIP_UDP(ip, port, true);
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_FileNotFound", sender, NULL, ip);
+ if (sender && sender->UDPPacketPending()){
+ sender->UDPReaskFNF(); // may delete 'sender'!
+ sender = NULL;
+ }
+ else if (sender != NULL)
+ DebugLogError(_T("Received UDP Packet (OP_FILENOTFOUND) which was not requested (pendingflag == false); Ignored packet - %s"), sender->DbgGetClientInfo());
+
+ break;
+ }
+ case OP_PORTTEST:
+ {
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_PortTest", NULL, NULL, ip);
+ theStats.AddDownDataOverheadOther(size);
+ if (size == 1){
+ if (packet[0] == 0x12){
+ bool ret = theApp.listensocket->SendPortTestReply('1', true);
+ AddDebugLogLine(true, _T("UDP Portcheck packet arrived - ACK sent back (status=%i)"), ret);
+ }
+ }
+ break;
+ }
+ case OP_DIRECTCALLBACKREQ:
+ {
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ DebugRecv("OP_DIRECTCALLBACKREQ", NULL, NULL, ip);
+ if (!theApp.clientlist->AllowCalbackRequest(ip)){
+ DebugLogWarning(_T("Ignored DirectCallback Request because this IP (%s) has sent too many request within a short time"), ipstr(ip));
+ break;
+ }
+ // do we accept callbackrequests at all?
+ if (Kademlia::CKademlia::IsRunning() && Kademlia::CKademlia::IsFirewalled())
+ {
+ theApp.clientlist->AddTrackCallbackRequests(ip);
+ CSafeMemFile data(packet, size);
+ uint16 nRemoteTCPPort = data.ReadUInt16();
+ uchar uchUserHash[16];
+ data.ReadHash16(uchUserHash);
+ uint8 byConnectOptions = data.ReadUInt8();
+ CUpDownClient* pRequester = theApp.clientlist->FindClientByUserHash(uchUserHash, ip, nRemoteTCPPort);
+ if (pRequester == NULL) {
+ pRequester = new CUpDownClient(NULL, nRemoteTCPPort, ip, 0, 0, true);
+ pRequester->SetUserHash(uchUserHash);
+ theApp.clientlist->AddClient(pRequester);
+ }
+ pRequester->SetConnectOptions(byConnectOptions, true, false);
+ pRequester->SetDirectUDPCallbackSupport(false);
+ pRequester->SetIP(ip);
+ pRequester->SetUserPort(nRemoteTCPPort);
+ DEBUG_ONLY( DebugLog(_T("Accepting incoming DirectCallbackRequest from %s"), pRequester->DbgGetClientInfo()) );
+ pRequester->TryToConnect();
+ }
+ else
+ DebugLogWarning(_T("Ignored DirectCallback Request because we do not accept DirectCall backs at all (%s)"), ipstr(ip));
+
+ break;
+ }
+ default:
+ theStats.AddDownDataOverheadOther(size);
+ if (thePrefs.GetDebugClientUDPLevel() > 0)
+ {
+ CUpDownClient* sender = theApp.downloadqueue->GetDownloadClientByIP_UDP(ip, port, true);
+ Debug(_T("Unknown client UDP packet: host=%s:%u (%s) opcode=0x%02x size=%u\n"), ipstr(ip), port, sender ? sender->DbgGetClientInfo() : _T(""), opcode, size);
+ }
+ return false;
+ }
+ return true;
+}
+
+void CClientUDPSocket::OnSend(int nErrorCode){
+ if (nErrorCode){
+ if (thePrefs.GetVerbose())
+ DebugLogError(_T("Error: Client UDP socket, error on send event: %s"), GetErrorMessage(nErrorCode, 1));
+ return;
+ }
+
+// ZZ:UploadBandWithThrottler (UDP) -->
+ sendLocker.Lock();
+ m_bWouldBlock = false;
+
+ if(!controlpacket_queue.IsEmpty()) {
+ theApp.uploadBandwidthThrottler->QueueForSendingControlPacket(this);
+ }
+ sendLocker.Unlock();
+// <-- ZZ:UploadBandWithThrottler (UDP)
+}
+
+SocketSentBytes CClientUDPSocket::SendControlData(uint32 maxNumberOfBytesToSend, uint32 /*minFragSize*/){ // ZZ:UploadBandWithThrottler (UDP)
+// ZZ:UploadBandWithThrottler (UDP) -->
+ // NOTE: *** This function is invoked from a *different* thread!
+ sendLocker.Lock();
+
+ uint32 sentBytes = 0;
+// <-- ZZ:UploadBandWithThrottler (UDP)
+
+ while (!controlpacket_queue.IsEmpty() && !IsBusy() && sentBytes < maxNumberOfBytesToSend){ // ZZ:UploadBandWithThrottler (UDP)
+ UDPPack* cur_packet = controlpacket_queue.GetHead();
+ if( GetTickCount() - cur_packet->dwTime < UDPMAXQUEUETIME )
+ {
+ uint32 nLen = cur_packet->packet->size+2;
+ uchar* sendbuffer = new uchar[nLen];
+ memcpy(sendbuffer,cur_packet->packet->GetUDPHeader(),2);
+ memcpy(sendbuffer+2,cur_packet->packet->pBuffer,cur_packet->packet->size);
+
+ if (cur_packet->bEncrypt && (theApp.GetPublicIP() > 0 || cur_packet->bKad)){
+ nLen = EncryptSendClient(&sendbuffer, nLen, cur_packet->pachTargetClientHashORKadID, cur_packet->bKad, cur_packet->nReceiverVerifyKey, (cur_packet->bKad ? Kademlia::CPrefs::GetUDPVerifyKey(cur_packet->dwIP) : (uint16)0));
+ //DEBUG_ONLY( AddDebugLogLine(DLP_VERYLOW, false, _T("Sent obfuscated UDP packet to clientIP: %s, Kad: %s, ReceiverKey: %u"), ipstr(cur_packet->dwIP), cur_packet->bKad ? _T("Yes") : _T("No"), cur_packet->nReceiverVerifyKey) );
+ }
+
+ if (!SendTo((char*)sendbuffer, nLen, cur_packet->dwIP, cur_packet->nPort)){
+ sentBytes += nLen; // ZZ:UploadBandWithThrottler (UDP)
+
+ controlpacket_queue.RemoveHead();
+ delete cur_packet->packet;
+ delete cur_packet;
+ }
+ delete[] sendbuffer;
+ }
+ else
+ {
+ controlpacket_queue.RemoveHead();
+ delete cur_packet->packet;
+ delete cur_packet;
+ }
+ }
+
+// ZZ:UploadBandWithThrottler (UDP) -->
+ if(!IsBusy() && !controlpacket_queue.IsEmpty()) {
+ theApp.uploadBandwidthThrottler->QueueForSendingControlPacket(this);
+ }
+ sendLocker.Unlock();
+
+ SocketSentBytes returnVal = { true, 0, sentBytes };
+ return returnVal;
+// <-- ZZ:UploadBandWithThrottler (UDP)
+}
+
+int CClientUDPSocket::SendTo(char* lpBuf,int nBufLen,uint32 dwIP, uint16 nPort){
+ // NOTE: *** This function is invoked from a *different* thread!
+ uint32 result = CAsyncSocket::SendTo(lpBuf,nBufLen,nPort,ipstr(dwIP));
+ if (result == (uint32)SOCKET_ERROR){
+ uint32 error = GetLastError();
+ if (error == WSAEWOULDBLOCK){
+ m_bWouldBlock = true;
+ return -1;
+ }
+ if (thePrefs.GetVerbose())
+ DebugLogError(_T("Error: Client UDP socket, failed to send data to %s:%u: %s"), ipstr(dwIP), nPort, GetErrorMessage(error, 1));
+ }
+ return 0;
+}
+
+bool CClientUDPSocket::SendPacket(Packet* packet, uint32 dwIP, uint16 nPort, bool bEncrypt, const uchar* pachTargetClientHashORKadID, bool bKad, uint32 nReceiverVerifyKey){
+ UDPPack* newpending = new UDPPack;
+ newpending->dwIP = dwIP;
+ newpending->nPort = nPort;
+ newpending->packet = packet;
+ newpending->dwTime = GetTickCount();
+ newpending->bEncrypt = bEncrypt && (pachTargetClientHashORKadID != NULL || (bKad && nReceiverVerifyKey != 0));
+ newpending->bKad = bKad;
+ newpending->nReceiverVerifyKey = nReceiverVerifyKey;
+
+#ifdef _DEBUG
+ if (newpending->packet->size > UDP_KAD_MAXFRAGMENT)
+ DebugLogWarning(_T("Sending UDP packet > UDP_KAD_MAXFRAGMENT, opcode: %X, size: %u"), packet->opcode, packet->size);
+#endif
+
+ if (newpending->bEncrypt && pachTargetClientHashORKadID != NULL)
+ md4cpy(newpending->pachTargetClientHashORKadID, pachTargetClientHashORKadID);
+ else
+ md4clr(newpending->pachTargetClientHashORKadID);
+// ZZ:UploadBandWithThrottler (UDP) -->
+ sendLocker.Lock();
+ controlpacket_queue.AddTail(newpending);
+ sendLocker.Unlock();
+
+ theApp.uploadBandwidthThrottler->QueueForSendingControlPacket(this);
+ return true;
+// <-- ZZ:UploadBandWithThrottler (UDP)
+}
+
+bool CClientUDPSocket::Create()
+{
+ bool ret = true;
+
+ if (thePrefs.GetUDPPort())
+ {
+ ret = CAsyncSocket::Create(thePrefs.GetUDPPort(), SOCK_DGRAM, FD_READ | FD_WRITE, thePrefs.GetBindAddrW()) != FALSE;
+ if (ret)
+ {
+ m_port = thePrefs.GetUDPPort();
+ // the default socket size seems to be not enough for this UDP socket
+ // because we tend to drop packets if several flow in at the same time
+ int val = 64 * 1024;
+ if (!SetSockOpt(SO_RCVBUF, &val, sizeof(val)))
+ DebugLogError(_T("Failed to increase socket size on UDP socket"));
+ }
+ }
+
+ if (ret)
+ m_port = thePrefs.GetUDPPort();
+
+ return ret;
+}
+
+bool CClientUDPSocket::Rebind()
+{
+ if (thePrefs.GetUDPPort() == m_port)
+ return false;
+ Close();
+ return Create();
+}
diff --git a/ClientUDPSocket.h b/ClientUDPSocket.h
new file mode 100644
index 00000000..f04d3995
--- /dev/null
+++ b/ClientUDPSocket.h
@@ -0,0 +1,65 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "UploadBandwidthThrottler.h" // ZZ:UploadBandWithThrottler (UDP)
+#include "EncryptedDatagramSocket.h"
+
+class Packet;
+
+#pragma pack(1)
+struct UDPPack
+{
+ Packet* packet;
+ uint32 dwIP;
+ uint16 nPort;
+ uint32 dwTime;
+ bool bEncrypt;
+ bool bKad;
+ uint32 nReceiverVerifyKey;
+ uchar pachTargetClientHashORKadID[16];
+ //uint16 nPriority; We could add a priority system here to force some packets.
+};
+#pragma pack()
+
+class CClientUDPSocket : public CAsyncSocket, public CEncryptedDatagramSocket, public ThrottledControlSocket // ZZ:UploadBandWithThrottler (UDP)
+{
+public:
+ CClientUDPSocket();
+ virtual ~CClientUDPSocket();
+
+ bool Create();
+ bool Rebind();
+ uint16 GetConnectedPort() { return m_port; }
+ bool SendPacket(Packet* packet, uint32 dwIP, uint16 nPort, bool bEncrypt, const uchar* pachTargetClientHashORKadID, bool bKad, uint32 nReceiverVerifyKey);
+ SocketSentBytes SendControlData(uint32 maxNumberOfBytesToSend, uint32 minFragSize); // ZZ:UploadBandWithThrottler (UDP)
+
+protected:
+ bool ProcessPacket(const BYTE* packet, UINT size, uint8 opcode, uint32 ip, uint16 port);
+
+ virtual void OnSend(int nErrorCode);
+ virtual void OnReceive(int nErrorCode);
+
+private:
+ int SendTo(char* lpBuf,int nBufLen,uint32 dwIP, uint16 nPort);
+ bool IsBusy() const { return m_bWouldBlock; }
+ bool m_bWouldBlock;
+ uint16 m_port;
+
+ CTypedPtrList controlpacket_queue;
+
+ CCriticalSection sendLocker; // ZZ:UploadBandWithThrottler (UDP)
+};
diff --git a/ClientVersionInfo.h b/ClientVersionInfo.h
new file mode 100644
index 00000000..62c860cb
--- /dev/null
+++ b/ClientVersionInfo.h
@@ -0,0 +1,177 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+
+
+#pragma once
+#include "emule.h"
+#include "updownclient.h"
+// class to convert peercache Client versions to comparable values.
+
+#define CVI_IGNORED -1
+// == means same client type, same or ignored version (for example eMule/0.4.* == eMule/0.4.2 )
+// != means different client or different defined version (for example eMule/0.4.2 != SomeClient/0.4.2 )
+// > mean _same client type_ and higher version, which therefor cannot be completely undefined ( for example eMule/1.* > eMule/0.4.2 )
+// >= same as > but here the version can be undefined ( for example eMule/* >= eMule/0.4.2 )
+class CClientVersionInfo{
+public:
+
+ CClientVersionInfo(CString strPCEncodedVersion)
+ {
+ m_nVerMajor = (UINT)CVI_IGNORED;
+ m_nVerMinor = (UINT)CVI_IGNORED;
+ m_nVerUpdate = (UINT)CVI_IGNORED;
+ m_nVerBuild = (UINT)CVI_IGNORED;
+ m_ClientTypeMajor = SO_UNKNOWN;
+ m_ClientTypeMinor = SO_UNKNOWN;
+
+ int posSeperator = strPCEncodedVersion.Find('/',1);
+ if (posSeperator == (-1) || strPCEncodedVersion.GetLength() - posSeperator < 2){
+ theApp.QueueDebugLogLine( false, _T("PeerCache Error: Bad Version info in PeerCache Descriptor found: %s"), strPCEncodedVersion);
+ return;
+ }
+ CString strClientType = strPCEncodedVersion.Left(posSeperator).Trim();
+ CString strVersionNumber = strPCEncodedVersion.Mid(posSeperator+1).Trim();
+
+ if (strClientType.CompareNoCase(_T("eMule")) == 0)
+ m_ClientTypeMajor = SO_EMULE;
+ else if (strClientType.CompareNoCase(_T("eDonkey")) == 0)
+ m_ClientTypeMajor = SO_EDONKEYHYBRID;
+ // can add more types here
+ else{
+ theApp.QueueDebugLogLine(false, _T("PeerCache Warning: Unknown Clienttype in descriptor file found"));
+ m_ClientTypeMajor = SO_UNKNOWN;
+ }
+
+ int curPos2= 0;
+ CString strNumber = strVersionNumber.Tokenize(_T("."),curPos2);
+ if (strNumber.IsEmpty())
+ return;
+ else if (strNumber == _T("*"))
+ m_nVerMajor = (UINT)-1;
+ else
+ m_nVerMajor = _tstoi(strNumber);
+ strNumber = strVersionNumber.Tokenize(_T("."),curPos2);
+ if (strNumber.IsEmpty())
+ return;
+ else if (strNumber == _T("*"))
+ m_nVerMinor = (UINT)-1;
+ else
+ m_nVerMinor = _tstoi(strNumber);
+ strNumber = strVersionNumber.Tokenize(_T("."),curPos2);
+ if (strNumber.IsEmpty())
+ return;
+ else if (strNumber == _T("*"))
+ m_nVerUpdate = (UINT)-1;
+ else
+ m_nVerUpdate = _tstoi(strNumber);
+ strNumber = strVersionNumber.Tokenize(_T("."),curPos2);
+ if (strNumber.IsEmpty())
+ return;
+ else if (strNumber == _T("*"))
+ m_nVerBuild = (UINT)-1;
+ else
+ m_nVerBuild = _tstoi(strNumber);
+ }
+
+ CClientVersionInfo(uint32 dwTagVersionInfo, UINT nClientMajor)
+ {
+ UINT nClientMajVersion = (dwTagVersionInfo >> 17) & 0x7f;
+ UINT nClientMinVersion = (dwTagVersionInfo>> 10) & 0x7f;
+ UINT nClientUpVersion = (dwTagVersionInfo >> 7) & 0x07;
+ CClientVersionInfo(nClientMajVersion, nClientMinVersion, nClientUpVersion, (UINT)CVI_IGNORED, nClientMajor, SO_UNKNOWN);
+ }
+
+ CClientVersionInfo(uint32 nVerMajor, uint32 nVerMinor, uint32 nVerUpdate, uint32 nVerBuild, uint32 ClientTypeMajor, uint32 ClientTypeMinor = SO_UNKNOWN)
+ {
+ m_nVerMajor = nVerMajor;
+ m_nVerMinor = nVerMinor;
+ m_nVerUpdate = nVerUpdate;
+ m_nVerBuild = nVerBuild;
+ m_ClientTypeMajor = ClientTypeMajor;
+ m_ClientTypeMinor = ClientTypeMinor;
+ }
+
+ CClientVersionInfo(){
+ CClientVersionInfo((UINT)CVI_IGNORED, (UINT)CVI_IGNORED, (UINT)CVI_IGNORED, (UINT)CVI_IGNORED, SO_UNKNOWN, SO_UNKNOWN);
+ }
+
+ CClientVersionInfo(const CClientVersionInfo& cv) {*this = cv;}
+
+ CClientVersionInfo& operator=(const CClientVersionInfo& cv)
+ {
+ m_nVerMajor = cv.m_nVerMajor;
+ m_nVerMinor = cv.m_nVerMinor;
+ m_nVerUpdate = cv.m_nVerUpdate;
+ m_nVerBuild = cv.m_nVerBuild;
+ m_ClientTypeMajor = cv.m_ClientTypeMajor;
+ m_ClientTypeMinor = cv.m_ClientTypeMinor;
+ return *this;
+ }
+
+ friend bool operator==(const CClientVersionInfo& c1, const CClientVersionInfo& c2)
+ {
+ return ( (c1.m_nVerMajor == (-1) || c2.m_nVerMajor == (-1) || c1.m_nVerMajor == c2.m_nVerMajor)
+ && (c1.m_nVerMinor == (-1) || c2.m_nVerMinor == (-1) || c1.m_nVerMinor == c2.m_nVerMinor)
+ && (c1.m_nVerUpdate == (-1) || c2.m_nVerUpdate == (-1) || c1.m_nVerUpdate == c2.m_nVerUpdate)
+ && (c1.m_nVerBuild == (-1) || c2.m_nVerBuild == (-1) || c1.m_nVerBuild == c2.m_nVerBuild)
+ && (c1.m_ClientTypeMajor == (-1) || c2.m_ClientTypeMajor == (-1) || c1.m_ClientTypeMajor == c2.m_ClientTypeMajor)
+ && (c1.m_ClientTypeMinor == (-1) || c2.m_ClientTypeMinor == (-1) || c1.m_ClientTypeMinor == c2.m_ClientTypeMinor)
+ );
+ }
+
+ friend bool operator !=(const CClientVersionInfo& c1, const CClientVersionInfo& c2)
+ {
+ return !(c1 == c2);
+ }
+
+ friend bool operator >(const CClientVersionInfo& c1, const CClientVersionInfo& c2)
+ {
+ if ( (c1.m_ClientTypeMajor == (-1) || c2.m_ClientTypeMajor == (-1) || c1.m_ClientTypeMajor != c2.m_ClientTypeMajor)
+ || (c1.m_ClientTypeMinor != c2.m_ClientTypeMinor))
+ return false;
+ if (c1.m_nVerMajor != (-1) && c2.m_nVerMajor != (-1) && c1.m_nVerMajor > c2.m_nVerMajor)
+ return true;
+ if (c1.m_nVerMinor != (-1) && c2.m_nVerMinor != (-1) && c1.m_nVerMinor > c2.m_nVerMinor)
+ return true;
+ if (c1.m_nVerUpdate != (-1) && c2.m_nVerUpdate != (-1) && c1.m_nVerUpdate > c2.m_nVerUpdate)
+ return true;
+ if (c1.m_nVerBuild != (-1) && c2.m_nVerBuild != (-1) && c1.m_nVerBuild > c2.m_nVerBuild)
+ return true;
+ return false;
+ }
+
+ friend bool operator <(const CClientVersionInfo& c1, const CClientVersionInfo& c2)
+ {
+ return c2 > c1;
+ }
+
+ friend bool operator <=(const CClientVersionInfo& c1, const CClientVersionInfo& c2)
+ {
+ return c2 > c1 || c1 == c2;
+ }
+
+ friend bool operator >=(const CClientVersionInfo& c1, const CClientVersionInfo& c2)
+ {
+ return c1 > c2 || c1 == c2;
+ }
+
+ UINT m_nVerMajor;
+ UINT m_nVerMinor;
+ UINT m_nVerUpdate;
+ UINT m_nVerBuild;
+ UINT m_ClientTypeMajor;
+ UINT m_ClientTypeMinor; //unused atm
+};
\ No newline at end of file
diff --git a/ClosableTabCtrl.cpp b/ClosableTabCtrl.cpp
new file mode 100644
index 00000000..f86f23c9
--- /dev/null
+++ b/ClosableTabCtrl.cpp
@@ -0,0 +1,561 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "ClosableTabCtrl.h"
+#include "OtherFunctions.h"
+#include "MenuCmds.h"
+#include "UserMsgs.h"
+#include "VisualStylesXP.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+// _WIN32_WINNT >= 0x0501 (XP only)
+#define _WM_THEMECHANGED 0x031A
+#define _ON_WM_THEMECHANGED() \
+ { _WM_THEMECHANGED, 0, 0, 0, AfxSig_l, \
+ (AFX_PMSG)(AFX_PMSGW) \
+ (static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(void) > (_OnThemeChanged)) \
+ },
+
+///////////////////////////////////////////////////////////////////////////////
+// CClosableTabCtrl
+
+IMPLEMENT_DYNAMIC(CClosableTabCtrl, CTabCtrl)
+
+BEGIN_MESSAGE_MAP(CClosableTabCtrl, CTabCtrl)
+ ON_WM_LBUTTONUP()
+ ON_WM_LBUTTONDBLCLK()
+ ON_WM_MBUTTONUP()
+ ON_WM_CREATE()
+ ON_WM_SYSCOLORCHANGE()
+ ON_WM_CONTEXTMENU()
+ _ON_WM_THEMECHANGED()
+ ON_WM_CTLCOLOR_REFLECT()
+ ON_WM_CTLCOLOR()
+ ON_WM_ERASEBKGND()
+ ON_WM_MEASUREITEM()
+ ON_WM_MEASUREITEM_REFLECT()
+END_MESSAGE_MAP()
+
+CClosableTabCtrl::CClosableTabCtrl()
+{
+ m_bCloseable = true;
+ memset(&m_iiCloseButton, 0, sizeof m_iiCloseButton);
+ m_ptCtxMenu.SetPoint(-1, -1);
+}
+
+CClosableTabCtrl::~CClosableTabCtrl()
+{
+}
+
+void CClosableTabCtrl::GetCloseButtonRect(int iItem, const CRect& rcItem, CRect& rcCloseButton, bool bItemSelected, bool bVistaThemeActive)
+{
+ rcCloseButton.top = rcItem.top + 2;
+ rcCloseButton.bottom = rcCloseButton.top + (m_iiCloseButton.rcImage.bottom - m_iiCloseButton.rcImage.top);
+ rcCloseButton.right = rcItem.right - 2;
+ rcCloseButton.left = rcCloseButton.right - (m_iiCloseButton.rcImage.right - m_iiCloseButton.rcImage.left);
+ if (bVistaThemeActive)
+ rcCloseButton.left -= 1; // the close button does not look 'symetric' with a width of 16, give it 17
+ if (bItemSelected) {
+ rcCloseButton.OffsetRect(-1, 0);
+ if (bVistaThemeActive) {
+ int iItems = GetItemCount();
+ if (iItems > 1 && iItem == iItems - 1)
+ rcCloseButton.OffsetRect(-2, 0);
+ }
+ }
+ else {
+ if (bVistaThemeActive) {
+ int iItems = GetItemCount();
+ if (iItems > 1 && iItem < iItems - 1)
+ rcCloseButton.OffsetRect(2, 0);
+ }
+ }
+}
+
+int CClosableTabCtrl::GetTabUnderPoint(CPoint point) const
+{
+ int iTabs = GetItemCount();
+ for (int i = 0; i < iTabs; i++)
+ {
+ CRect rcItem;
+ GetItemRect(i, rcItem);
+ rcItem.InflateRect(2, 2); // get the real tab item rect
+ if (rcItem.PtInRect(point))
+ return i;
+ }
+ return -1;
+}
+
+int CClosableTabCtrl::GetTabUnderContextMenu() const
+{
+ if (m_ptCtxMenu.x == -1 || m_ptCtxMenu.y == -1)
+ return -1;
+ return GetTabUnderPoint(m_ptCtxMenu);
+}
+
+bool CClosableTabCtrl::SetDefaultContextMenuPos()
+{
+ int iTab = GetCurSel();
+ if (iTab != -1)
+ {
+ CRect rcItem;
+ if (GetItemRect(iTab, &rcItem))
+ {
+ rcItem.InflateRect(2, 2); // get the real tab item rect
+ m_ptCtxMenu.x = rcItem.left + rcItem.Width()/2;
+ m_ptCtxMenu.y = rcItem.top + rcItem.Height()/2;
+ return true;
+ }
+ }
+ return false;
+}
+
+void CClosableTabCtrl::OnMButtonUp(UINT nFlags, CPoint point)
+{
+ if (m_bCloseable)
+ {
+ int iTab = GetTabUnderPoint(point);
+ if (iTab != -1) {
+ GetParent()->SendMessage(UM_CLOSETAB, (WPARAM)iTab);
+ return;
+ }
+ }
+
+ CTabCtrl::OnMButtonUp(nFlags, point);
+}
+
+void CClosableTabCtrl::OnLButtonUp(UINT nFlags, CPoint point)
+{
+ if (m_bCloseable)
+ {
+ int iTab = GetTabUnderPoint(point);
+ if (iTab != -1)
+ {
+ CRect rcItem;
+ GetItemRect(iTab, rcItem);
+ rcItem.InflateRect(2, 2); // get the real tab item rect
+
+ bool bVistaThemeActive = theApp.IsVistaThemeActive();
+ CRect rcCloseButton;
+ GetCloseButtonRect(iTab, rcItem, rcCloseButton, iTab == GetCurSel(), bVistaThemeActive);
+
+ // The visible part of our close icon is one pixel less on each side
+ if (!bVistaThemeActive) {
+ rcCloseButton.top += 1;
+ rcCloseButton.left += 1;
+ rcCloseButton.right -= 1;
+ rcCloseButton.bottom -= 1;
+ }
+
+ if (rcCloseButton.PtInRect(point)) {
+ GetParent()->SendMessage(UM_CLOSETAB, (WPARAM)iTab);
+ return;
+ }
+ }
+ }
+
+ CTabCtrl::OnLButtonUp(nFlags, point);
+}
+
+void CClosableTabCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
+{
+ int iTab = GetTabUnderPoint(point);
+ if (iTab != -1) {
+ GetParent()->SendMessage(UM_DBLCLICKTAB, (WPARAM)iTab);
+ return;
+ }
+ CTabCtrl::OnLButtonDblClk(nFlags, point);
+}
+
+// It would be nice if there would the option to restrict the maximum width of a tab control.
+// We would need that feature actually for almost all our tab controls. Especially for the
+// search results list - those tab control labels can get quite large. But I did not yet a
+// find a way to limit the width of tabs. Although MSDN says that an owner drawn
+// tab control receives a WM_MEASUREITEM, I never got one.
+
+// Vista: This gets never called for an owner drawn tab control
+void CClosableTabCtrl::OnMeasureItem(int iCtlId, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
+{
+ TRACE("CClosableTabCtrl::OnMeasureItem\n");
+ __super::OnMeasureItem(iCtlId, lpMeasureItemStruct);
+}
+
+// Vista: This gets never called for an owner drawn tab control
+void CClosableTabCtrl::MeasureItem(LPMEASUREITEMSTRUCT)
+{
+ TRACE("CClosableTabCtrl::MeasureItem\n");
+}
+
+void CClosableTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDIS)
+{
+ CRect rect(lpDIS->rcItem);
+ int nTabIndex = lpDIS->itemID;
+ if (nTabIndex < 0)
+ return;
+
+ TCHAR szLabel[256];
+ TC_ITEM tci;
+ tci.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_STATE;
+ tci.pszText = szLabel;
+ tci.cchTextMax = _countof(szLabel);
+ tci.dwStateMask = TCIS_HIGHLIGHTED;
+ if (!GetItem(nTabIndex, &tci))
+ return;
+ szLabel[_countof(szLabel) - 1] = _T('\0');
+ //TRACE("CClosableTabCtrl::DrawItem: item=%u, state=%08x, color=%08x, rc=%3d,%3d,%3dx%3d\n", nTabIndex, tci.dwState, GetTextColor(lpDIS->hDC), lpDIS->rcItem.left, lpDIS->rcItem.top, lpDIS->rcItem.right - lpDIS->rcItem.left, lpDIS->rcItem.bottom - lpDIS->rcItem.top);
+
+ CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+ if (!pDC)
+ return;
+
+ CRect rcFullItem(lpDIS->rcItem);
+ bool bSelected = (lpDIS->itemState & ODS_SELECTED) != 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // Adding support for XP Styles (Vista Themes) for owner drawn tab controls simply
+ // does *not* work under Vista. Maybe it works under XP (did not try), but that is
+ // meaningless because under XP a owner drawn tab control is already rendered *with*
+ // the proper XP Styles. So, for XP there is no need to care about the theme API at all.
+ //
+ // However, under Vista, a tab control which has the TCS_OWNERDRAWFIXED
+ // style gets additional 3D-borders which are applied by Vista *after* WM_DRAWITEM
+ // was processed. Thus, there is no known workaround available to prevent Vista from
+ // adding those old fashioned 3D-borders. We can render the tab control items within
+ // the WM_DRAWITEM handler in whatever style we want, but Vista will in each case
+ // overwrite the borders of each tab control item with old fashioned 3D-borders...
+ //
+ // To complete this experience, tab controls also do not support NMCUSTOMDRAW. So, the
+ // only known way to customize a tab control is by using TCS_OWNERDRAWFIXED which does
+ // however not work properly under Vista.
+ //
+ // The "solution" which is currently implemented to prevent Vista from drawing those
+ // 3D-borders is by using "ExcludeClipRect" to reduce the drawing area which is used
+ // by Windows after WM_DRAWITEM was processed. This "solution" is very sensitive to
+ // the used rectangles and offsets in general. Incrementing/Decrementing one of the
+ // "rcItem", "rcFullItem", etc. rectangles makes the entire "solution" flawed again
+ // because some borders would become visible again.
+ //
+ HTHEME hTheme = NULL;
+ int iPartId = TABP_TABITEM;
+ int iStateId = TIS_NORMAL;
+ bool bVistaHotTracked = false;
+ bool bVistaThemeActive = theApp.IsVistaThemeActive();
+ if (bVistaThemeActive)
+ {
+ // To determine if the current item is in 'hot tracking' mode, we need to evaluate
+ // the current foreground color - there is no flag which would indicate this state
+ // more safely. This applies only for Vista and for tab controls which have the
+ // TCS_OWNERDRAWFIXED style.
+ bVistaHotTracked = pDC->GetTextColor() == GetSysColor(COLOR_HOTLIGHT);
+
+ hTheme = g_xpStyle.OpenThemeData(m_hWnd, L"TAB");
+ if (hTheme)
+ {
+ if (bSelected) {
+ // get the real tab item rect
+ rcFullItem.left += 1;
+ rcFullItem.right -= 1;
+ rcFullItem.bottom -= 1;
+ }
+ else
+ rcFullItem.InflateRect(2, 2); // get the real tab item rect
+
+ CRect rcBk(rcFullItem);
+ if (bSelected)
+ {
+ iStateId = TTIS_SELECTED;
+ if (nTabIndex == 0) {
+ // First item
+ if (nTabIndex == GetItemCount() - 1)
+ iPartId = TABP_TOPTABITEMBOTHEDGE; // First & Last item
+ else
+ iPartId = TABP_TOPTABITEMLEFTEDGE;
+ }
+ else if (nTabIndex == GetItemCount() - 1) {
+ // Last item
+ iPartId = TABP_TOPTABITEMRIGHTEDGE;
+ }
+ else {
+ iPartId = TABP_TOPTABITEM;
+ }
+ }
+ else
+ {
+ rcBk.top += 2;
+ iStateId = bVistaHotTracked ? TIS_HOT : TIS_NORMAL;
+ if (nTabIndex == 0) {
+ // First item
+ if (nTabIndex == GetItemCount() - 1)
+ iPartId = TABP_TABITEMBOTHEDGE; // First & Last item
+ else
+ iPartId = TABP_TABITEMLEFTEDGE;
+ }
+ else if (nTabIndex == GetItemCount() - 1) {
+ // Last item
+ iPartId = TABP_TABITEMRIGHTEDGE;
+ }
+ else {
+ iPartId = TABP_TABITEM;
+ }
+ }
+ if (g_xpStyle.IsThemeBackgroundPartiallyTransparent(hTheme, iPartId, iStateId))
+ g_xpStyle.DrawThemeParentBackground(m_hWnd, *pDC, &rcFullItem);
+ g_xpStyle.DrawThemeBackground(hTheme, *pDC, iPartId, iStateId, &rcBk, NULL);
+ }
+ }
+
+ // Following background clearing is needed for:
+ // WinXP/Vista (when used without an application theme)
+ // Vista (when used with an application theme but without a theme for the tab control)
+ if ( (!g_xpStyle.IsThemeActive() || !g_xpStyle.IsAppThemed())
+ || (hTheme == NULL && bVistaThemeActive) )
+ pDC->FillSolidRect(&lpDIS->rcItem, GetSysColor(COLOR_BTNFACE));
+
+ int iOldBkMode = pDC->SetBkMode(TRANSPARENT);
+
+ // Draw image on left side
+ CImageList *piml = GetImageList();
+ if (tci.iImage >= 0 && piml && piml->m_hImageList)
+ {
+ IMAGEINFO ii;
+ piml->GetImageInfo(0, &ii);
+ rect.left += bSelected ? 8 : 4;
+ piml->Draw(pDC, tci.iImage, CPoint(rect.left, rect.top + 2), ILD_TRANSPARENT);
+ rect.left += (ii.rcImage.right - ii.rcImage.left);
+ if (!bSelected)
+ rect.left += 4;
+ }
+
+ bool bCloseable = m_bCloseable;
+ if (bCloseable && GetParent()->SendMessage(UM_QUERYTAB, nTabIndex))
+ bCloseable = false;
+
+ // Draw 'Close button' at right side
+ if (bCloseable && m_ImgLstCloseButton.m_hImageList)
+ {
+ CRect rcCloseButton;
+ GetCloseButtonRect(nTabIndex, rect, rcCloseButton, bSelected, bVistaThemeActive);
+
+ HTHEME hThemeNC = bVistaThemeActive ? g_xpStyle.OpenThemeData(m_hWnd, _T("WINDOW")) : NULL;
+ if (hThemeNC) {
+ // Possible "Close" parts: WP_CLOSEBUTTON, WP_SMALLCLOSEBUTTON, WP_MDICLOSEBUTTON
+ int iPartId = WP_SMALLCLOSEBUTTON;
+ int iStateId = (bSelected || bVistaHotTracked) ? CBS_NORMAL : CBS_DISABLED;
+ if (g_xpStyle.IsThemeBackgroundPartiallyTransparent(hTheme, iPartId, iStateId))
+ g_xpStyle.DrawThemeParentBackground(m_hWnd, *pDC, &rcCloseButton);
+ g_xpStyle.DrawThemeBackground(hThemeNC, *pDC, iPartId, iStateId, rcCloseButton, NULL);
+ g_xpStyle.CloseThemeData(hThemeNC);
+ }
+ else {
+ m_ImgLstCloseButton.Draw(pDC, (bSelected || bVistaHotTracked) ? 0 : 1, rcCloseButton.TopLeft(), ILD_TRANSPARENT);
+ }
+
+ rect.right = rcCloseButton.left - 2;
+ if (bSelected)
+ rect.left += hTheme ? 4 : 2;
+ }
+
+ COLORREF crOldColor = CLR_NONE;
+ if (tci.dwState & TCIS_HIGHLIGHTED)
+ crOldColor = pDC->SetTextColor(RGB(192, 0, 0));
+ else if (bVistaHotTracked)
+ crOldColor = pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
+
+ rect.top += bSelected ? 4 : 3;
+ // Vista: Tab control has troubles with determining the width of a tab if the
+ // label contains one '&' character. To get around this, we use the old code which
+ // replaces one '&' character with two '&' characters and we do not specify DT_NOPREFIX
+ // here when drawing the text.
+ //
+ // Vista: "DrawThemeText" can not be used in case we need a certain foreground color. Thus we always us
+ // "DrawText" to always get the same font and metrics (just for safety).
+ pDC->DrawText(szLabel, rect, DT_SINGLELINE | DT_TOP | DT_CENTER /*| DT_NOPREFIX*/);
+
+ if (crOldColor != CLR_NONE)
+ pDC->SetTextColor(crOldColor);
+ pDC->SetBkMode(iOldBkMode);
+
+ if (hTheme)
+ {
+ CRect rcClip(rcFullItem);
+ if (bSelected) {
+ rcClip.left -= 2 + 1;
+ rcClip.right += 2 + 1;
+ }
+ else {
+ rcClip.top += 2;
+ }
+ pDC->ExcludeClipRect(&rcClip);
+ g_xpStyle.CloseThemeData(hTheme);
+ }
+}
+
+void CClosableTabCtrl::PreSubclassWindow()
+{
+ CTabCtrl::PreSubclassWindow();
+ InternalInit();
+}
+
+int CClosableTabCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ if (CTabCtrl::OnCreate(lpCreateStruct) == -1)
+ return -1;
+ InternalInit();
+ return 0;
+}
+
+void CClosableTabCtrl::InternalInit()
+{
+ ModifyStyle(0, TCS_OWNERDRAWFIXED);
+
+#if 1
+ // Under Vista Aero, all tab controls get by default the TCS_HOTTRACK
+ // style even if it was not specified within the resource file. Though, to 'see'
+ // the hot tracking effect the control also need to get initialized explicitly with
+ // the WS_CLIPCHILDREN style within a *seperate* function call. Yes, there is no
+ // logic to all this, not at all. It simply is that way.
+ //
+ // So, do *not* "optimize" that code by using only one "ModifyStyle" function call.
+ // The 2nd function call to "ModifyStyle" is very much by intention!
+ //
+ // However, the hot tracking effect which is achived this way does not survive a
+ // theme change. After the theme is changed (regardless whether we switch between
+ // Vista themes or from/to a non-Vista theme), the hot tracking effect is gone even
+ // if we try to modify the styles again within OnThemeChanged...
+ if (theApp.IsVistaThemeActive())
+ ModifyStyle(0, WS_CLIPCHILDREN);
+#else
+ // Remove the automatically applied hot tracking effect to avoid that the tab control
+ // may use it when it also sets the WS_CLIPCHILDREN (for other reasons) later.
+ ModifyStyle(TCS_HOTTRACK, 0);
+#endif
+
+ SetAllIcons();
+}
+
+void CClosableTabCtrl::OnSysColorChange()
+{
+ CTabCtrl::OnSysColorChange();
+ SetAllIcons();
+}
+
+void CClosableTabCtrl::SetAllIcons()
+{
+ if (m_bCloseable)
+ {
+ const int iIconWidth = 16;
+ const int iIconHeight = 16;
+ m_ImgLstCloseButton.DeleteImageList();
+ m_ImgLstCloseButton.Create(iIconWidth, iIconHeight, theApp.m_iDfltImageListColorFlags | ILC_MASK, 0, 1);
+ m_ImgLstCloseButton.Add(CTempIconLoader(_T("CloseTabSelected"), iIconWidth, iIconHeight));
+ m_ImgLstCloseButton.Add(CTempIconLoader(_T("CloseTab"), iIconWidth, iIconHeight));
+ m_ImgLstCloseButton.GetImageInfo(0, &m_iiCloseButton);
+ Invalidate();
+ }
+}
+
+void CClosableTabCtrl::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
+{
+ if (m_bCloseable)
+ {
+ if (point.x == -1 || point.y == -1) {
+ if (!SetDefaultContextMenuPos())
+ return;
+ point = m_ptCtxMenu;
+ ClientToScreen(&point);
+ }
+ else {
+ m_ptCtxMenu = point;
+ ScreenToClient(&m_ptCtxMenu);
+ }
+
+ int iTab = GetTabUnderPoint(m_ptCtxMenu);
+ if (iTab != -1)
+ {
+ if (GetParent()->SendMessage(UM_QUERYTAB, (WPARAM)iTab) == 0)
+ {
+ CMenu menu;
+ menu.CreatePopupMenu();
+ menu.AppendMenu(MF_STRING, MP_REMOVE, GetResString(IDS_FD_CLOSE));
+ menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
+ }
+ }
+ }
+}
+
+BOOL CClosableTabCtrl::OnCommand(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam == MP_REMOVE)
+ {
+ if (m_ptCtxMenu.x != -1 && m_ptCtxMenu.y != -1)
+ {
+ int iTab = GetTabUnderPoint(m_ptCtxMenu);
+ if (iTab != -1) {
+ GetParent()->SendMessage(UM_CLOSETAB, (WPARAM)iTab);
+ return TRUE;
+ }
+ }
+ }
+ return CTabCtrl::OnCommand(wParam, lParam);
+}
+
+LRESULT CClosableTabCtrl::_OnThemeChanged()
+{
+ // Owner drawn tab control seems to have troubles with updating itself due to an XP theme change..
+ ModifyStyle(TCS_OWNERDRAWFIXED, 0); // Reset control style to not-owner drawn
+ Default(); // Process original WM_THEMECHANGED message
+ ModifyStyle(0, TCS_OWNERDRAWFIXED); // Apply owner drawn style again
+ return 0;
+}
+
+// Vista: This gets never called for an owner drawn tab control
+HBRUSH CClosableTabCtrl::CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/)
+{
+ // Change any attributes of the DC here
+ // Return a non-NULL brush if the parent's handler should not be called
+ return NULL;
+}
+
+// Vista: This gets never called for an owner drawn tab control
+HBRUSH CClosableTabCtrl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = CTabCtrl::OnCtlColor(pDC, pWnd, nCtlColor);
+ // Change any attributes of the DC here
+ // Return a different brush if the default is not desired
+ return hbr;
+}
+
+// Vista: Can not be used to workaround the problems with owner drawn tab control
+BOOL CClosableTabCtrl::OnEraseBkgnd(CDC* pDC)
+{
+ return CTabCtrl::OnEraseBkgnd(pDC);
+}
+
+BOOL CClosableTabCtrl::DeleteItem(int nItem)
+{
+ // if we remove a tab which would lead to scrolling back to other tabs, all those become hidden for... whatever reasons
+ // its easy enough wo work arround by scrolling to the first visible tab _before_ we delete the other one
+ SetCurSel(0);
+ return __super::DeleteItem(nItem);
+}
diff --git a/ClosableTabCtrl.h b/ClosableTabCtrl.h
new file mode 100644
index 00000000..2b21eecb
--- /dev/null
+++ b/ClosableTabCtrl.h
@@ -0,0 +1,43 @@
+#pragma once
+
+class CClosableTabCtrl : public CTabCtrl
+{
+ DECLARE_DYNAMIC(CClosableTabCtrl)
+
+public:
+ CClosableTabCtrl();
+ virtual ~CClosableTabCtrl();
+ BOOL DeleteItem(int nItem);
+
+ bool m_bCloseable;
+
+protected:
+ CImageList m_ImgLstCloseButton;
+ IMAGEINFO m_iiCloseButton;
+ CPoint m_ptCtxMenu;
+
+ void InternalInit();
+ void SetAllIcons();
+ void GetCloseButtonRect(int iItem, const CRect& rcItem, CRect& rcCloseButton, bool bItemSelected, bool bVistaThemeActive);
+ int GetTabUnderContextMenu() const;
+ int GetTabUnderPoint(CPoint point) const;
+ bool SetDefaultContextMenuPos();
+
+ virtual void PreSubclassWindow();
+ virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
+ afx_msg void OnMButtonUp(UINT nFlags, CPoint point);
+ afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
+ afx_msg void OnSysColorChange();
+ afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
+ afx_msg LRESULT _OnThemeChanged();
+ afx_msg HBRUSH CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/);
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+ afx_msg void OnMeasureItem(int, LPMEASUREITEMSTRUCT);
+ afx_msg void MeasureItem(LPMEASUREITEMSTRUCT);
+};
diff --git a/Collection.cpp b/Collection.cpp
new file mode 100644
index 00000000..7cf7515b
--- /dev/null
+++ b/Collection.cpp
@@ -0,0 +1,446 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#include "StdAfx.h"
+#include "collection.h"
+#include "KnownFile.h"
+#include "CollectionFile.h"
+#include "SafeFile.h"
+#include "Packets.h"
+#include "Preferences.h"
+#include "SharedFilelist.h"
+#include "emule.h"
+#include "Log.h"
+#include "md5sum.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define COLLECTION_FILE_VERSION1_INITIAL 0x01
+#define COLLECTION_FILE_VERSION2_LARGEFILES 0x02
+
+CCollection::CCollection(void)
+: m_sCollectionName(_T(""))
+, m_sCollectionAuthorName(_T(""))
+, m_bTextFormat(false)
+{
+ m_CollectionFilesMap.InitHashTable(1031);
+ m_sCollectionName.Format(_T("New Collection-%u"), ::GetTickCount());
+ m_pabyCollectionAuthorKey = NULL;
+ m_nKeySize = 0;
+}
+
+CCollection::CCollection(const CCollection* pCollection)
+{
+ m_sCollectionName = pCollection->m_sCollectionName;
+ if (pCollection->m_pabyCollectionAuthorKey != NULL){
+ m_nKeySize = pCollection->m_nKeySize;
+ m_pabyCollectionAuthorKey = new BYTE[m_nKeySize];
+ memcpy(m_pabyCollectionAuthorKey, pCollection->m_pabyCollectionAuthorKey, m_nKeySize);
+ m_sCollectionAuthorName = pCollection->m_sCollectionAuthorName;
+ }
+ else{
+ m_nKeySize = 0;
+ m_pabyCollectionAuthorKey = NULL;
+ }
+
+ m_bTextFormat = pCollection->m_bTextFormat;
+
+ m_CollectionFilesMap.InitHashTable(1031);
+ POSITION pos = pCollection->m_CollectionFilesMap.GetStartPosition();
+ CCollectionFile* pCollectionFile;
+ CSKey key;
+ while( pos != NULL )
+ {
+ pCollection->m_CollectionFilesMap.GetNextAssoc( pos, key, pCollectionFile );
+ AddFileToCollection(pCollectionFile, true);
+ }
+}
+
+CCollection::~CCollection(void)
+{
+ delete[] m_pabyCollectionAuthorKey;
+ POSITION pos = m_CollectionFilesMap.GetStartPosition();
+ CCollectionFile* pCollectionFile;
+ CSKey key;
+ while( pos != NULL )
+ {
+ m_CollectionFilesMap.GetNextAssoc( pos, key, pCollectionFile );
+ delete pCollectionFile;
+ }
+ m_CollectionFilesMap.RemoveAll();
+}
+
+CCollectionFile* CCollection::AddFileToCollection(CAbstractFile* pAbstractFile, bool bCreateClone)
+{
+ CSKey key(pAbstractFile->GetFileHash());
+ CCollectionFile* pCollectionFile;
+ if (m_CollectionFilesMap.Lookup(key, pCollectionFile))
+ {
+ ASSERT(0);
+ return pCollectionFile;
+ }
+
+ pCollectionFile = NULL;
+
+ if(bCreateClone)
+ pCollectionFile = new CCollectionFile(pAbstractFile);
+ else if(pAbstractFile->IsKindOf(RUNTIME_CLASS(CCollectionFile)))
+ pCollectionFile = (CCollectionFile*)pAbstractFile;
+
+ if(pCollectionFile)
+ m_CollectionFilesMap.SetAt(key, pCollectionFile);
+
+ return pCollectionFile;
+}
+
+void CCollection::RemoveFileFromCollection(CAbstractFile* pAbstractFile)
+{
+ CSKey key(pAbstractFile->GetFileHash());
+ CCollectionFile* pCollectionFile;
+ if (m_CollectionFilesMap.Lookup(key, pCollectionFile))
+ {
+ m_CollectionFilesMap.RemoveKey(key);
+ delete pCollectionFile;
+ }
+ else
+ ASSERT(0);
+}
+
+void CCollection::SetCollectionAuthorKey(const byte* abyCollectionAuthorKey, uint32 nSize)
+{
+ delete[] m_pabyCollectionAuthorKey;
+ m_pabyCollectionAuthorKey = NULL;
+ m_nKeySize = 0;
+ if (abyCollectionAuthorKey != NULL){
+ m_pabyCollectionAuthorKey = new BYTE[nSize];
+ memcpy(m_pabyCollectionAuthorKey, abyCollectionAuthorKey, nSize);
+ m_nKeySize = nSize;
+ }
+}
+
+bool CCollection::InitCollectionFromFile(const CString& sFilePath, CString sFileName)
+{
+ DEBUG_ONLY( sFileName.Replace(COLLECTION_FILEEXTENSION, _T("")) );
+
+ bool bCollectionLoaded = false;
+
+ CSafeFile data;
+ if(data.Open(sFilePath, CFile::modeRead | CFile::shareDenyWrite | CFile::typeBinary))
+ {
+ try
+ {
+ uint32 nVersion = data.ReadUInt32();
+ if(nVersion == COLLECTION_FILE_VERSION1_INITIAL || nVersion == COLLECTION_FILE_VERSION2_LARGEFILES)
+ {
+ uint32 headerTagCount = data.ReadUInt32();
+ while(headerTagCount)
+ {
+ CTag tag(&data, true);
+ switch(tag.GetNameID())
+ {
+ case FT_FILENAME:
+ {
+ if(tag.IsStr())
+ m_sCollectionName = tag.GetStr();
+ break;
+ }
+ case FT_COLLECTIONAUTHOR:
+ {
+ if(tag.IsStr())
+ m_sCollectionAuthorName = tag.GetStr();
+ break;
+ }
+ case FT_COLLECTIONAUTHORKEY:
+ {
+ if(tag.IsBlob())
+ {
+ SetCollectionAuthorKey(tag.GetBlob(), tag.GetBlobSize());
+ }
+ break;
+ }
+ }
+ headerTagCount--;
+ }
+ uint32 fileCount = data.ReadUInt32();
+ while(fileCount)
+ {
+ CCollectionFile* pCollectionFile = new CCollectionFile(&data);
+ if(pCollectionFile)
+ AddFileToCollection(pCollectionFile, false);
+ fileCount--;
+ }
+ bCollectionLoaded = true;
+ }
+ if (m_pabyCollectionAuthorKey != NULL){
+ bool bResult = false;
+ if (data.GetLength() > data.GetPosition()){
+ using namespace CryptoPP;
+
+ uint32 nPos = (uint32)data.GetPosition();
+ data.SeekToBegin();
+ BYTE* pMessage = new BYTE[nPos];
+ VERIFY( data.Read(pMessage, nPos) == nPos);
+
+ StringSource ss_Pubkey(m_pabyCollectionAuthorKey, m_nKeySize, true, 0);
+ RSASSA_PKCS1v15_SHA_Verifier pubkey(ss_Pubkey);
+
+ int nSignLen = (int)(data.GetLength() - data.GetPosition());
+ BYTE* pSignature = new BYTE[nSignLen ];
+ VERIFY( data.Read(pSignature, nSignLen) == (UINT)nSignLen);
+
+ bResult = pubkey.VerifyMessage(pMessage, nPos, pSignature, nSignLen);
+
+ delete[] pMessage;
+ delete[] pSignature;
+ }
+ if (!bResult){
+ DebugLogWarning(_T("Collection %s: Verifying of public key failed!"), m_sCollectionName);
+ delete[] m_pabyCollectionAuthorKey;
+ m_pabyCollectionAuthorKey = NULL;
+ m_nKeySize = 0;
+ m_sCollectionAuthorName = _T("");
+ }
+ else
+ DebugLog(_T("Collection %s: Public key verified"), m_sCollectionName);
+
+ }
+ else
+ m_sCollectionAuthorName = _T("");
+ data.Close();
+ }
+ catch(CFileException* error)
+ {
+ error->Delete();
+ return false;
+ }
+ catch(...)
+ {
+ ASSERT( false );
+ data.Close();
+ return false;
+ }
+ }
+ else
+ return false;
+
+ if(!bCollectionLoaded)
+ {
+ CStdioFile data;
+ if(data.Open(sFilePath, CFile::modeRead | CFile::shareDenyWrite | CFile::typeText))
+ {
+ try
+ {
+ CString sLink;
+ while(data.ReadString(sLink))
+ {
+ //Ignore all lines that start with #.
+ //These lines can be used for future features..
+ if(sLink.Find(_T("#")) != 0)
+ {
+ try
+ {
+ CCollectionFile* pCollectionFile = new CCollectionFile();
+ if (pCollectionFile->InitFromLink(sLink))
+ AddFileToCollection(pCollectionFile, false);
+ else
+ delete pCollectionFile;
+ }
+ catch(...)
+ {
+ ASSERT( false );
+ data.Close();
+ return false;
+ }
+ }
+ }
+ data.Close();
+ m_sCollectionName = sFileName;
+ bCollectionLoaded = true;
+ m_bTextFormat = true;
+ }
+ catch(CFileException* error)
+ {
+ error->Delete();
+ return false;
+ }
+ catch(...)
+ {
+ ASSERT( false );
+ data.Close();
+ return false;
+ }
+ }
+ }
+
+ return bCollectionLoaded;
+}
+
+void CCollection::WriteToFileAddShared(CryptoPP::RSASSA_PKCS1v15_SHA_Signer* pSignKey)
+{
+ using namespace CryptoPP;
+ CString sFileName;
+ sFileName.Format(_T("%s%s"), m_sCollectionName, COLLECTION_FILEEXTENSION);
+
+ CString sFilePath;
+ sFilePath.Format(_T("%s\\%s"), thePrefs.GetMuleDirectory(EMULE_INCOMINGDIR), sFileName);
+
+ if(m_bTextFormat)
+ {
+ CStdioFile data;
+ if(data.Open(sFilePath, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite | CFile::typeText))
+ {
+ try
+ {
+ POSITION pos = m_CollectionFilesMap.GetStartPosition();
+ CCollectionFile* pCollectionFile;
+ CSKey key;
+ while( pos != NULL )
+ {
+ m_CollectionFilesMap.GetNextAssoc( pos, key, pCollectionFile );
+ CString sLink;
+ sLink.Format(_T("%s\n"), pCollectionFile->GetED2kLink());
+ data.WriteString(sLink);
+ }
+ data.Close();
+ }
+ catch(CFileException* error)
+ {
+ error->Delete();
+ return;
+ }
+ catch(...)
+ {
+ ASSERT( false );
+ data.Close();
+ return;
+ }
+ }
+ }
+ else
+ {
+ CSafeFile data;
+ if(data.Open(sFilePath, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite | CFile::typeBinary))
+ {
+ try
+ {
+ //Version
+ // check first if we have any large files in the map - write use lowest version possible
+ uint32 dwVersion = COLLECTION_FILE_VERSION1_INITIAL;
+ POSITION pos = m_CollectionFilesMap.GetStartPosition();
+ CCollectionFile* pCollectionFile;
+ CSKey key;
+ while( pos != NULL ) {
+ m_CollectionFilesMap.GetNextAssoc( pos, key, pCollectionFile );
+ if (pCollectionFile->IsLargeFile()){
+ dwVersion = COLLECTION_FILE_VERSION2_LARGEFILES;
+ break;
+ }
+ }
+ data.WriteUInt32(dwVersion);
+
+ uint32 uTagCount = 1;
+
+ //NumberHeaderTags
+ if(m_pabyCollectionAuthorKey != NULL)
+ uTagCount += 2;
+
+
+ data.WriteUInt32(uTagCount);
+
+ CTag collectionName(FT_FILENAME, m_sCollectionName);
+ collectionName.WriteTagToFile(&data, utf8strRaw);
+
+ if(m_pabyCollectionAuthorKey != NULL){
+ CTag collectionAuthor(FT_COLLECTIONAUTHOR, m_sCollectionAuthorName);
+ collectionAuthor.WriteTagToFile(&data, utf8strRaw);
+
+ CTag collectionAuthorKey(FT_COLLECTIONAUTHORKEY, m_nKeySize, m_pabyCollectionAuthorKey);
+ collectionAuthorKey.WriteTagToFile(&data, utf8strRaw);
+ }
+
+ //Total Files
+ data.WriteUInt32(m_CollectionFilesMap.GetSize());
+
+ pos = m_CollectionFilesMap.GetStartPosition();
+ while( pos != NULL ) {
+ m_CollectionFilesMap.GetNextAssoc( pos, key, pCollectionFile );
+ pCollectionFile->WriteCollectionInfo(&data);
+ }
+
+ if (pSignKey != NULL){
+ uint32 nPos = (uint32)data.GetPosition();
+ data.SeekToBegin();
+ BYTE* pBuffer = new BYTE[nPos];
+ VERIFY( data.Read(pBuffer, nPos) == nPos);
+
+ SecByteBlock sbbSignature(pSignKey->SignatureLength());
+ AutoSeededRandomPool rng;
+ pSignKey->SignMessage(rng, pBuffer ,nPos , sbbSignature.begin());
+ BYTE abyBuffer2[500];
+ ArraySink asink(abyBuffer2, 500);
+ asink.Put(sbbSignature.begin(), sbbSignature.size());
+ int nResult = (uint8)asink.TotalPutLength();
+ data.Write(abyBuffer2, nResult);
+
+ delete[] pBuffer;
+ }
+ data.Close();
+ }
+ catch(CFileException* error)
+ {
+ error->Delete();
+ return;
+ }
+ catch(...)
+ {
+ ASSERT( false );
+ data.Close();
+ return;
+ }
+ }
+ }
+
+ theApp.sharedfiles->AddFileFromNewlyCreatedCollection(sFilePath);
+}
+
+bool CCollection::HasCollectionExtention(const CString& sFileName)
+{
+ if(sFileName.Find(COLLECTION_FILEEXTENSION) == -1)
+ return false;
+ return true;
+}
+
+CString CCollection::GetCollectionAuthorKeyString(){
+ if (m_pabyCollectionAuthorKey != NULL)
+ return EncodeBase16(m_pabyCollectionAuthorKey, m_nKeySize);
+ else
+ return CString(_T(""));
+}
+
+CString CCollection::GetAuthorKeyHashString(){
+ if (m_pabyCollectionAuthorKey != NULL){
+ MD5Sum md5(m_pabyCollectionAuthorKey, m_nKeySize);
+ CString strResult = md5.GetHash();
+ strResult.MakeUpper();
+ return strResult;
+ }
+ return CString(_T(""));
+}
diff --git a/Collection.h b/Collection.h
new file mode 100644
index 00000000..cb01d69d
--- /dev/null
+++ b/Collection.h
@@ -0,0 +1,66 @@
+//this file is part of eMule
+//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "MapKey.h"
+#pragma warning(disable:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+#pragma warning(disable:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4702) // unreachable code
+#include
+#include
+#include
+#include
+#include
+#pragma warning(default:4702) // unreachable code
+#pragma warning(default:4100) // unreferenced formal parameter
+#pragma warning(default:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(default:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+
+#define COLLECTION_FILEEXTENSION _T(".emulecollection")
+
+class CAbstractFile;
+class CCollectionFile;
+
+typedef CMap CCollectionFilesMap;
+
+class CCollection
+{
+ friend class CCollectionCreateDialog;
+ friend class CCollectionViewDialog;
+public:
+ CCollection(void);
+ CCollection(const CCollection* pCollection);
+ ~CCollection(void);
+ bool InitCollectionFromFile(const CString& sFilePath, CString sFileName);
+ CCollectionFile* AddFileToCollection(CAbstractFile* pAbstractFile, bool bCreateClone);
+ void RemoveFileFromCollection(CAbstractFile* pAbstractFile);
+ void WriteToFileAddShared(CryptoPP::RSASSA_PKCS1v15_SHA_Signer* pSignkey = NULL);
+ void SetCollectionAuthorKey(const byte* abyCollectionAuthorKey, uint32 nSize);
+ CString GetCollectionAuthorKeyString();
+ static bool HasCollectionExtention(const CString& sFileName);
+ CString GetAuthorKeyHashString();
+
+ CString m_sCollectionName;
+ CString m_sCollectionAuthorName;
+
+ bool m_bTextFormat;
+
+private:
+ CCollectionFilesMap m_CollectionFilesMap;
+ byte* m_pabyCollectionAuthorKey;
+ uint32 m_nKeySize;
+};
diff --git a/CollectionCreateDialog.cpp b/CollectionCreateDialog.cpp
new file mode 100644
index 00000000..c8fc6b63
--- /dev/null
+++ b/CollectionCreateDialog.cpp
@@ -0,0 +1,421 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "emuledlg.h"
+#include "CollectionCreateDialog.h"
+#include "OtherFunctions.h"
+#include "Collection.h"
+#include "Sharedfilelist.h"
+#include "CollectionFile.h"
+#include "KnownFile.h"
+#include "KnownFileList.h"
+#include "PartFile.h"
+#include "TransferDlg.h"
+#include "DownloadListCtrl.h"
+#pragma warning(disable:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+#pragma warning(disable:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4702) // unreachable code
+#include
+#include
+#include
+#include
+#include
+#pragma warning(default:4702) // unreachable code
+#pragma warning(default:4100) // unreferenced formal parameter
+#pragma warning(default:4244) // conversion from 'type1' to 'type2', possible loss of data
+#pragma warning(default:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
+#include "Preferences.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define PREF_INI_SECTION _T("CollectionCreateDlg")
+
+enum ECols
+{
+ colName = 0,
+ colSize,
+ colHash
+};
+
+IMPLEMENT_DYNAMIC(CCollectionCreateDialog, CDialog)
+
+BEGIN_MESSAGE_MAP(CCollectionCreateDialog, CResizableDialog)
+ ON_BN_CLICKED(IDC_CCOLL_CANCEL, OnCancel)
+ ON_BN_CLICKED(IDC_CCOLL_SAVE, OnBnClickedOk)
+ ON_BN_CLICKED(IDC_COLLECTIONADD, OnBnClickedCollectionAdd)
+ ON_BN_CLICKED(IDC_COLLECTIONCREATEFORMAT, OnBnClickedCollectionFormat)
+ ON_BN_CLICKED(IDC_COLLECTIONREMOVE, OnBnClickedCollectionRemove)
+ ON_BN_CLICKED(IDC_COLLECTIONVIEWSHAREBUTTON, OnBnClickedCollectionViewShared)
+ ON_EN_KILLFOCUS(IDC_COLLECTIONNAMEEDIT, OnEnKillFocusCollectionName)
+ ON_NOTIFY(NM_DBLCLK, IDC_COLLECTIONAVAILLIST, OnNmDblClkCollectionAvailList)
+ ON_NOTIFY(NM_DBLCLK, IDC_COLLECTIONLISTCTRL, OnNmDblClkCollectionList)
+END_MESSAGE_MAP()
+
+CCollectionCreateDialog::CCollectionCreateDialog(CWnd* pParent /*=NULL*/)
+ : CResizableDialog(CCollectionCreateDialog::IDD, pParent)
+ , m_pCollection(NULL)
+ , m_bSharedFiles(false)
+{
+ m_icoWnd = NULL;
+ m_icoForward = NULL;
+ m_icoBack = NULL;
+ m_icoColl = NULL;
+ m_icoFiles = NULL;
+ m_bCreatemode = false;
+}
+
+CCollectionCreateDialog::~CCollectionCreateDialog()
+{
+ if (m_icoWnd)
+ VERIFY( DestroyIcon(m_icoWnd) );
+ if (m_icoForward)
+ VERIFY( DestroyIcon(m_icoForward) );
+ if (m_icoBack)
+ VERIFY( DestroyIcon(m_icoBack) );
+ if (m_icoColl)
+ VERIFY( DestroyIcon(m_icoColl) );
+ if (m_icoFiles)
+ VERIFY( DestroyIcon(m_icoFiles) );
+}
+
+void CCollectionCreateDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_COLLECTIONLISTCTRL, m_CollectionListCtrl);
+ DDX_Control(pDX, IDC_COLLECTIONAVAILLIST, m_CollectionAvailListCtrl);
+ DDX_Control(pDX, IDC_COLLECTIONNAMEEDIT, m_CollectionNameEdit);
+ DDX_Control(pDX, IDC_COLLECTIONVIEWSHAREBUTTON, m_CollectionViewShareButton);
+ DDX_Control(pDX, IDC_COLLECTIONADD, m_AddCollectionButton);
+ DDX_Control(pDX, IDC_COLLECTIONREMOVE, m_RemoveCollectionButton);
+ DDX_Control(pDX, IDC_COLLECTIONLISTLABEL, m_CollectionListLabel);
+ DDX_Control(pDX, IDC_CCOLL_SAVE, m_SaveButton);
+ DDX_Control(pDX, IDC_CCOLL_CANCEL, m_CancelButton);
+ DDX_Control(pDX, IDC_COLLECTIONLISTICON, m_CollectionListIcon);
+ DDX_Control(pDX, IDC_COLLECTIONSOURCELISTICON, m_CollectionSourceListIcon);
+ DDX_Control(pDX, IDC_COLLECTIONCREATESIGNCHECK, m_CollectionCreateSignNameKeyCheck);
+ DDX_Control(pDX, IDC_COLLECTIONCREATEFORMAT, m_CollectionCreateFormatCheck);
+}
+
+void CCollectionCreateDialog::SetCollection(CCollection* pCollection, bool create)
+{
+ if (!pCollection) {
+ ASSERT(0);
+ return;
+ }
+ m_pCollection = pCollection;
+ m_bCreatemode = create;
+}
+
+BOOL CCollectionCreateDialog::OnInitDialog(void)
+{
+ CDialog::OnInitDialog();
+ InitWindowStyles(this);
+
+ if (!m_pCollection) {
+ ASSERT(0);
+ return TRUE;
+ }
+ SetIcon(m_icoWnd = theApp.LoadIcon(_T("AABCollectionFileType")), FALSE);
+ if (m_bCreatemode)
+ SetWindowText(GetResString(IDS_CREATECOLLECTION));
+ else
+ SetWindowText(GetResString(IDS_MODIFYCOLLECTION) + _T(": ") + m_pCollection->m_sCollectionName);
+
+ m_CollectionListCtrl.Init(_T("CollectionCreateR"));
+ m_CollectionAvailListCtrl.Init(_T("CollectionCreateL"));
+
+ m_AddCollectionButton.SetIcon(m_icoForward = theApp.LoadIcon(_T("FORWARD")));
+ m_RemoveCollectionButton.SetIcon(m_icoBack = theApp.LoadIcon(_T("BACK")));
+ m_CollectionListIcon.SetIcon(m_icoColl = theApp.LoadIcon(_T("AABCollectionFileType")));
+ m_CollectionSourceListIcon.SetIcon(m_icoFiles = theApp.LoadIcon(_T("SharedFilesList")));
+
+ m_SaveButton.SetWindowText(GetResString(IDS_SAVE));
+ m_CancelButton.SetWindowText(GetResString(IDS_CANCEL));
+ m_CollectionCreateSignNameKeyCheck.SetWindowText(GetResString(IDS_COLL_SIGN));
+ m_CollectionCreateFormatCheck.SetWindowText(GetResString(IDS_COLL_TEXTFORMAT));
+ SetDlgItemText(IDC_CCOLL_STATIC_NAME, GetResString(IDS_SW_NAME) + _T(":"));
+ SetDlgItemText(IDC_CCOLL_BASICOPTIONS, GetResString(IDS_LD_BASICOPT));
+ SetDlgItemText(IDC_CCOLL_ADVANCEDOPTIONS, GetResString(IDS_LD_ADVANCEDOPT));
+
+ AddAnchor(IDC_COLLECTIONAVAILLIST, TOP_LEFT, BOTTOM_CENTER);
+ AddAnchor(IDC_COLLECTIONLISTCTRL, TOP_CENTER, BOTTOM_RIGHT);
+ AddAnchor(IDC_COLLECTIONLISTLABEL, TOP_CENTER);
+ AddAnchor(IDC_COLLECTIONLISTICON, TOP_CENTER);
+ AddAnchor(IDC_COLLECTIONADD, MIDDLE_CENTER);
+ AddAnchor(IDC_COLLECTIONREMOVE, MIDDLE_CENTER);
+ AddAnchor(IDC_CCOLL_SAVE, BOTTOM_RIGHT);
+ AddAnchor(IDC_CCOLL_CANCEL, BOTTOM_RIGHT);
+ AddAnchor(IDC_CCOLL_BASICOPTIONS, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_CCOLL_ADVANCEDOPTIONS, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_CCOLL_STATIC_NAME, BOTTOM_LEFT);
+ AddAnchor(IDC_COLLECTIONNAMEEDIT, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_COLLECTIONCREATEFORMAT, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_COLLECTIONCREATESIGNCHECK, BOTTOM_LEFT, BOTTOM_RIGHT);
+ EnableSaveRestore(PREF_INI_SECTION);
+
+ POSITION pos = m_pCollection->m_CollectionFilesMap.GetStartPosition();
+ while (pos != NULL)
+ {
+ CSKey key;
+ CCollectionFile* pCollectionFile;
+ m_pCollection->m_CollectionFilesMap.GetNextAssoc(pos, key, pCollectionFile);
+ m_CollectionListCtrl.AddFileToList(pCollectionFile);
+ }
+ CString strTitle;
+ strTitle.Format(GetResString(IDS_COLLECTIONLIST) + _T(" (%u)"), m_CollectionListCtrl.GetItemCount());
+ m_CollectionListLabel.SetWindowText(strTitle);
+
+ OnBnClickedCollectionViewShared();
+
+ m_CollectionNameEdit.SetWindowText(::CleanupFilename(m_pCollection->m_sCollectionName));
+ m_CollectionCreateFormatCheck.SetCheck(m_pCollection->m_bTextFormat);
+ OnBnClickedCollectionFormat();
+ GetDlgItem(IDC_CCOLL_SAVE)->EnableWindow(m_CollectionListCtrl.GetItemCount() > 0);
+
+ return TRUE;
+}
+
+void CCollectionCreateDialog::AddSelectedFiles(void)
+{
+ CTypedPtrList knownFileList;
+ POSITION pos = m_CollectionAvailListCtrl.GetFirstSelectedItemPosition();
+ while (pos != NULL)
+ {
+ int index = m_CollectionAvailListCtrl.GetNextSelectedItem(pos);
+ if (index >= 0)
+ knownFileList.AddTail((CKnownFile*)m_CollectionAvailListCtrl.GetItemData(index));
+ }
+
+ while (knownFileList.GetCount() > 0)
+ {
+ CAbstractFile* pAbstractFile = knownFileList.RemoveHead();
+ CCollectionFile* pCollectionFile = m_pCollection->AddFileToCollection(pAbstractFile, true);
+ if (pCollectionFile)
+ m_CollectionListCtrl.AddFileToList(pCollectionFile);
+ }
+
+ CString strTitle;
+ strTitle.Format(GetResString(IDS_COLLECTIONLIST) + _T(" (%u)"), m_CollectionListCtrl.GetItemCount());
+ m_CollectionListLabel.SetWindowText(strTitle);
+
+ GetDlgItem(IDC_CCOLL_SAVE)->EnableWindow(m_CollectionListCtrl.GetItemCount() > 0);
+}
+
+void CCollectionCreateDialog::RemoveSelectedFiles(void)
+{
+ CTypedPtrList collectionFileList;
+ POSITION pos = m_CollectionListCtrl.GetFirstSelectedItemPosition();
+ while (pos != NULL)
+ {
+ int index = m_CollectionListCtrl.GetNextSelectedItem(pos);
+ if (index >= 0)
+ collectionFileList.AddTail((CCollectionFile*)m_CollectionListCtrl.GetItemData(index));
+ }
+
+ while (collectionFileList.GetCount() > 0)
+ {
+ CCollectionFile* pCollectionFile = collectionFileList.RemoveHead();
+ m_CollectionListCtrl.RemoveFileFromList(pCollectionFile);
+ m_pCollection->RemoveFileFromCollection(pCollectionFile);
+ }
+
+ CString strTitle;
+ strTitle.Format(GetResString(IDS_COLLECTIONLIST) + _T(" (%u)"), m_CollectionListCtrl.GetItemCount());
+ m_CollectionListLabel.SetWindowText(strTitle);
+ GetDlgItem(IDC_CCOLL_SAVE)->EnableWindow(m_CollectionListCtrl.GetItemCount() > 0);
+}
+
+void CCollectionCreateDialog::OnBnClickedCollectionRemove()
+{
+ RemoveSelectedFiles();
+}
+
+void CCollectionCreateDialog::OnBnClickedCollectionAdd()
+{
+ AddSelectedFiles();
+}
+
+void CCollectionCreateDialog::OnBnClickedOk()
+{
+ //Some users have noted that the collection can at times
+ //save a collection with a invalid name...
+ OnEnKillFocusCollectionName();
+
+ CString sFileName;
+ m_CollectionNameEdit.GetWindowText(sFileName);
+ if (!sFileName.IsEmpty())
+ {
+ m_pCollection->m_sCollectionAuthorName.Empty();
+ m_pCollection->SetCollectionAuthorKey(NULL, 0);
+ m_pCollection->m_sCollectionName = sFileName;
+ m_pCollection->m_bTextFormat = (m_CollectionCreateFormatCheck.GetCheck() == BST_CHECKED);
+
+ CString sFilePath;
+ sFilePath.Format(_T("%s\\%s.emulecollection"), thePrefs.GetMuleDirectory(EMULE_INCOMINGDIR), m_pCollection->m_sCollectionName);
+
+ using namespace CryptoPP;
+ RSASSA_PKCS1v15_SHA_Signer* pSignkey = NULL;
+ if (m_CollectionCreateSignNameKeyCheck.GetCheck())
+ {
+ bool bCreateNewKey = false;
+ HANDLE hKeyFile = ::CreateFile(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + _T("collectioncryptkey.dat"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hKeyFile != INVALID_HANDLE_VALUE)
+ {
+ if (::GetFileSize(hKeyFile, NULL) == 0)
+ bCreateNewKey = true;
+ ::CloseHandle(hKeyFile);
+ }
+ else
+ bCreateNewKey = true;
+
+ if (bCreateNewKey)
+ {
+ try
+ {
+ AutoSeededRandomPool rng;
+ InvertibleRSAFunction privkey;
+ privkey.Initialize(rng, 1024);
+ Base64Encoder privkeysink(new FileSink(CStringA(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + _T("collectioncryptkey.dat"))));
+ privkey.DEREncode(privkeysink);
+ privkeysink.MessageEnd();
+ }
+ catch(...)
+ {
+ ASSERT(0);
+ }
+ }
+
+ try
+ {
+ FileSource filesource(CStringA(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + _T("collectioncryptkey.dat")), true,new Base64Decoder);
+ pSignkey = new RSASSA_PKCS1v15_SHA_Signer(filesource);
+ RSASSA_PKCS1v15_SHA_Verifier pubkey(*pSignkey);
+ byte abyMyPublicKey[1000];
+ ArraySink asink(abyMyPublicKey, 1000);
+ pubkey.DEREncode(asink);
+ int nLen = asink.TotalPutLength();
+ asink.MessageEnd();
+ m_pCollection->SetCollectionAuthorKey(abyMyPublicKey, nLen);
+ }
+ catch(...)
+ {
+ ASSERT(0);
+ }
+
+ m_pCollection->m_sCollectionAuthorName = thePrefs.GetUserNick();
+ }
+
+ if (!PathFileExists(sFilePath))
+ {
+ m_pCollection->WriteToFileAddShared(pSignkey);
+ }
+ else
+ {
+ if (AfxMessageBox(GetResString(IDS_COLL_REPLACEEXISTING), MB_ICONWARNING | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_YESNO) == IDNO)
+ return;
+
+ bool bDeleteSuccessful = ShellDeleteFile(sFilePath);
+ if (bDeleteSuccessful)
+ {
+ CKnownFile* pKnownFile = theApp.knownfiles->FindKnownFileByPath(sFilePath);
+ if (pKnownFile)
+ {
+ theApp.sharedfiles->RemoveFile(pKnownFile, true);
+ if (pKnownFile->IsKindOf(RUNTIME_CLASS(CPartFile)))
+ theApp.emuledlg->transferwnd->GetDownloadList()->ClearCompleted(static_cast(pKnownFile));
+ }
+ m_pCollection->WriteToFileAddShared(pSignkey);
+ }
+ else
+ {
+ AfxMessageBox(GetResString(IDS_COLL_ERR_DELETING),MB_ICONWARNING | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_YESNO);
+ }
+ }
+
+ delete pSignkey;
+ pSignkey = NULL;
+
+ OnOK();
+ }
+}
+
+void CCollectionCreateDialog::UpdateAvailFiles(void)
+{
+ m_CollectionAvailListCtrl.DeleteAllItems();
+
+ CMap Files_Map;
+ if (m_bSharedFiles)
+ theApp.sharedfiles->CopySharedFileMap(Files_Map);
+ else
+ theApp.knownfiles->CopyKnownFileMap(Files_Map);
+
+ POSITION pos = Files_Map.GetStartPosition();
+ while (pos != NULL)
+ {
+ CCKey key;
+ CKnownFile* pKnownFile;
+ Files_Map.GetNextAssoc(pos, key, pKnownFile);
+ m_CollectionAvailListCtrl.AddFileToList(pKnownFile);
+ }
+}
+
+void CCollectionCreateDialog::OnBnClickedCollectionViewShared()
+{
+ m_bSharedFiles = !m_bSharedFiles;
+ UpdateAvailFiles();
+ CString strTitle;
+ strTitle.Format(_T(" ") + (m_bSharedFiles ? GetResString(IDS_SHARED) : GetResString(IDS_KNOWN)) + _T(" (%u)"), m_CollectionAvailListCtrl.GetItemCount());
+ m_CollectionViewShareButton.SetWindowText(strTitle);
+}
+
+void CCollectionCreateDialog::OnNmDblClkCollectionAvailList(NMHDR* /*pNMHDR*/, LRESULT* pResult)
+{
+ AddSelectedFiles();
+ *pResult = 0;
+}
+
+void CCollectionCreateDialog::OnNmDblClkCollectionList(NMHDR* /*pNMHDR*/, LRESULT* pResult)
+{
+ RemoveSelectedFiles();
+ *pResult = 0;
+}
+
+void CCollectionCreateDialog::OnEnKillFocusCollectionName()
+{
+ CString sFileName;
+ CString sNewFileName;
+ m_CollectionNameEdit.GetWindowText(sFileName);
+ sNewFileName = ValidFilename(sFileName);
+ if (sNewFileName.Compare(sFileName))
+ m_CollectionNameEdit.SetWindowText(sNewFileName);
+}
+
+void CCollectionCreateDialog::OnBnClickedCollectionFormat()
+{
+ if (m_CollectionCreateFormatCheck.GetCheck()) {
+ m_CollectionCreateSignNameKeyCheck.SetCheck(BST_UNCHECKED);
+ m_CollectionCreateSignNameKeyCheck.EnableWindow(FALSE);
+ }
+ else
+ m_CollectionCreateSignNameKeyCheck.EnableWindow(TRUE);
+}
diff --git a/CollectionCreateDialog.h b/CollectionCreateDialog.h
new file mode 100644
index 00000000..9b1c0343
--- /dev/null
+++ b/CollectionCreateDialog.h
@@ -0,0 +1,76 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "CollectionListCtrl.h"
+#include "ResizableLib\ResizableDialog.h"
+
+class CCollection;
+class CCollectionFile;
+
+class CCollectionCreateDialog : public CResizableDialog
+{
+ DECLARE_DYNAMIC(CCollectionCreateDialog)
+
+public:
+ CCollectionCreateDialog(CWnd* pParent = NULL); // standard constructor
+ virtual ~CCollectionCreateDialog();
+
+// Dialog Data
+ enum { IDD = IDD_COLLECTIONCREATEDIALOG };
+
+ void SetCollection(CCollection* pCollection, bool create);
+
+protected:
+ CCollection* m_pCollection;
+ CEdit m_CollectionNameEdit;
+ CCollectionListCtrl m_CollectionListCtrl;
+ CCollectionListCtrl m_CollectionAvailListCtrl;
+ bool m_bSharedFiles;
+ CButton m_CollectionViewShareButton;
+ CButton m_CollectionCreateFormatCheck;
+ HICON m_icoWnd;
+ HICON m_icoForward;
+ HICON m_icoBack;
+ HICON m_icoColl;
+ HICON m_icoFiles;
+ bool m_bCreatemode;
+ CButton m_AddCollectionButton;
+ CButton m_RemoveCollectionButton;
+ CStatic m_CollectionListLabel;
+ CButton m_SaveButton;
+ CButton m_CancelButton;
+ CStatic m_CollectionListIcon;
+ CStatic m_CollectionSourceListIcon;
+ CButton m_CollectionCreateSignNameKeyCheck;
+
+ void AddSelectedFiles(void);
+ void RemoveSelectedFiles(void);
+ void UpdateAvailFiles(void);
+
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual BOOL OnInitDialog(void);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnBnClickedCollectionAdd();
+ afx_msg void OnBnClickedCollectionFormat();
+ afx_msg void OnBnClickedCollectionRemove();
+ afx_msg void OnBnClickedCollectionViewShared();
+ afx_msg void OnBnClickedOk();
+ afx_msg void OnEnKillFocusCollectionName();
+ afx_msg void OnNmDblClkCollectionAvailList(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnNmDblClkCollectionList(NMHDR *pNMHDR, LRESULT *pResult);
+};
diff --git a/CollectionFile.cpp b/CollectionFile.cpp
new file mode 100644
index 00000000..c33f1886
--- /dev/null
+++ b/CollectionFile.cpp
@@ -0,0 +1,196 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#include "StdAfx.h"
+#include "collectionfile.h"
+#include "OtherFunctions.h"
+#include "Packets.h"
+#include "Ed2kLink.h"
+#include "resource.h"
+#include "Log.h"
+#include "Kademlia/Kademlia/Entry.h"
+#include "Kademlia/Kademlia/Tag.h"
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+IMPLEMENT_DYNAMIC(CCollectionFile, CAbstractFile)
+
+CCollectionFile::CCollectionFile(void)
+{
+}
+
+CCollectionFile::CCollectionFile(CFileDataIO* in_data)
+{
+ UINT tagcount = in_data->ReadUInt32();
+
+ for (UINT i = 0; i < tagcount; i++)
+ {
+ CTag* toadd = new CTag(in_data, true);
+ if (toadd)
+ taglist.Add(toadd);
+ }
+
+ CTag* pTagHash = GetTag(FT_FILEHASH);
+ if(pTagHash)
+ SetFileHash(pTagHash->GetHash());
+ else
+ ASSERT(0);
+
+ pTagHash = GetTag(FT_AICH_HASH);
+ if (pTagHash != NULL && pTagHash->IsStr())
+ {
+ CAICHHash hash;
+ if (DecodeBase32(pTagHash->GetStr(), hash) == (UINT)CAICHHash::GetHashSize())
+ m_FileIdentifier.SetAICHHash(hash);
+ else
+ ASSERT( false );
+ }
+
+ // here we have two choices
+ // - if the server/client sent us a filetype, we could use it (though it could be wrong)
+ // - we always trust our filetype list and determine the filetype by the extension of the file
+ //
+ // if we received a filetype from server, we use it.
+ // if we did not receive a filetype, we determine it by examining the file's extension.
+ //
+ // but, in no case, we will use the receive file type when adding this search result to the download queue, to avoid
+ // that we are using 'wrong' file types in part files. (this has to be handled when creating the part files)
+ const CString& rstrFileType = GetStrTagValue(FT_FILETYPE);
+ SetFileName(GetStrTagValue(FT_FILENAME), false, rstrFileType.IsEmpty());
+ SetFileSize(GetInt64TagValue(FT_FILESIZE));
+ if (!rstrFileType.IsEmpty())
+ {
+ if (_tcscmp(rstrFileType, _T(ED2KFTSTR_PROGRAM))==0)
+ {
+ CString strDetailFileType = GetFileTypeByName(GetFileName());
+ if (!strDetailFileType.IsEmpty())
+ SetFileType(strDetailFileType);
+ else
+ SetFileType(rstrFileType);
+ }
+ else
+ SetFileType(rstrFileType);
+ }
+
+ if(GetFileSize() == (uint64)0 || !GetFileName().Compare(_T("")))
+ ASSERT(0);
+}
+
+CCollectionFile::CCollectionFile(CAbstractFile* pAbstractFile) : CAbstractFile(pAbstractFile)
+{
+ ClearTags();
+
+ taglist.Add(new CTag(FT_FILEHASH, pAbstractFile->GetFileHash()));
+ taglist.Add(new CTag(FT_FILESIZE, pAbstractFile->GetFileSize(), true));
+ taglist.Add(new CTag(FT_FILENAME, pAbstractFile->GetFileName()));
+
+ if (m_FileIdentifier.HasAICHHash())
+ taglist.Add(new CTag(FT_AICH_HASH, m_FileIdentifier.GetAICHHash().GetString()));
+
+ if(!pAbstractFile->GetFileComment().IsEmpty())
+ taglist.Add(new CTag(FT_FILECOMMENT, pAbstractFile->GetFileComment()));
+
+ if(pAbstractFile->GetFileRating())
+ taglist.Add(new CTag(FT_FILERATING, pAbstractFile->GetFileRating()));
+
+ UpdateFileRatingCommentAvail();
+}
+
+bool CCollectionFile::InitFromLink(CString sLink)
+{
+ CED2KLink* pLink = NULL;
+ CED2KFileLink* pFileLink = NULL;
+ try
+ {
+ pLink = CED2KLink::CreateLinkFromUrl(sLink);
+ if(!pLink)
+ throw GetResString(IDS_ERR_NOTAFILELINK);
+ pFileLink = pLink->GetFileLink();
+ if (!pFileLink)
+ throw GetResString(IDS_ERR_NOTAFILELINK);
+ }
+ catch (CString error)
+ {
+ CString strBuffer;
+ strBuffer.Format(GetResString(IDS_ERR_INVALIDLINK),error);
+ LogError(LOG_STATUSBAR, GetResString(IDS_ERR_LINKERROR), strBuffer);
+ return false;
+ }
+
+ taglist.Add(new CTag(FT_FILEHASH, pFileLink->GetHashKey()));
+ m_FileIdentifier.SetMD4Hash(pFileLink->GetHashKey());
+
+ taglist.Add(new CTag(FT_FILESIZE, pFileLink->GetSize(), true));
+ SetFileSize(pFileLink->GetSize());
+
+ taglist.Add(new CTag(FT_FILENAME, pFileLink->GetName()));
+ SetFileName(pFileLink->GetName(), false, false);
+
+ if (pFileLink->HasValidAICHHash())
+ {
+ taglist.Add(new CTag(FT_AICH_HASH, pFileLink->GetAICHHash().GetString()));
+ m_FileIdentifier.SetAICHHash(pFileLink->GetAICHHash());
+ }
+
+ delete pLink;
+ return true;
+}
+
+CCollectionFile::~CCollectionFile(void)
+{
+}
+
+void CCollectionFile::WriteCollectionInfo(CFileDataIO *out_data)
+{
+ out_data->WriteUInt32(taglist.GetSize());
+
+ for (int i = 0; i < taglist.GetSize(); i++)
+ {
+ CTag tempTag(*taglist.GetAt(i));
+ tempTag.WriteNewEd2kTag(out_data, utf8strRaw);
+ }
+}
+
+void CCollectionFile::UpdateFileRatingCommentAvail(bool /*bForceUpdate*/)
+{
+ m_bHasComment = false;
+ UINT uRatings = 0;
+ UINT uUserRatings = 0;
+
+ for(POSITION pos = m_kadNotes.GetHeadPosition(); pos != NULL; )
+ {
+ Kademlia::CEntry* entry = m_kadNotes.GetNext(pos);
+ if (!m_bHasComment && !entry->GetStrTagValue(TAG_DESCRIPTION).IsEmpty())
+ m_bHasComment = true;
+ UINT rating = (UINT)entry->GetIntTagValue(TAG_FILERATING);
+ if (rating != 0)
+ {
+ uRatings++;
+ uUserRatings += rating;
+ }
+ }
+
+ if (uRatings)
+ m_uUserRating = uUserRatings / uRatings;
+ else
+ m_uUserRating = 0;
+}
diff --git a/CollectionFile.h b/CollectionFile.h
new file mode 100644
index 00000000..64152b84
--- /dev/null
+++ b/CollectionFile.h
@@ -0,0 +1,36 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#pragma once
+#include "abstractfile.h"
+
+class CFileDataIO;
+
+class CCollectionFile : public CAbstractFile
+{
+ DECLARE_DYNAMIC(CCollectionFile)
+
+public:
+ CCollectionFile(void);
+ CCollectionFile(CFileDataIO* in_data);
+ CCollectionFile(CAbstractFile* copyfrom);
+ virtual ~CCollectionFile(void);
+
+ bool InitFromLink(CString sLink);
+ void WriteCollectionInfo(CFileDataIO* out_data);
+ virtual void UpdateFileRatingCommentAvail(bool bForceUpdate = false);
+};
diff --git a/CollectionListCtrl.cpp b/CollectionListCtrl.cpp
new file mode 100644
index 00000000..5b81b70d
--- /dev/null
+++ b/CollectionListCtrl.cpp
@@ -0,0 +1,284 @@
+//this file is part of eMule
+//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "CollectionListCtrl.h"
+#include "OtherFunctions.h"
+#include "AbstractFile.h"
+#include "MetaDataDlg.h"
+#include "HighColorTab.hpp"
+#include "ListViewWalkerPropertySheet.h"
+#include "UserMsgs.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// CCollectionFileDetailsSheet
+
+class CCollectionFileDetailsSheet : public CListViewWalkerPropertySheet
+{
+ DECLARE_DYNAMIC(CCollectionFileDetailsSheet)
+
+public:
+ CCollectionFileDetailsSheet(CTypedPtrList& aFiles, UINT uPshInvokePage = 0, CListCtrlItemWalk* pListCtrl = NULL);
+ virtual ~CCollectionFileDetailsSheet();
+
+protected:
+ CMetaDataDlg m_wndMetaData;
+
+ UINT m_uPshInvokePage;
+ static LPCTSTR m_pPshStartPage;
+
+ void UpdateTitle();
+
+ virtual BOOL OnInitDialog();
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnDestroy();
+ afx_msg LRESULT OnDataChanged(WPARAM, LPARAM);
+};
+
+LPCTSTR CCollectionFileDetailsSheet::m_pPshStartPage;
+
+IMPLEMENT_DYNAMIC(CCollectionFileDetailsSheet, CListViewWalkerPropertySheet)
+
+BEGIN_MESSAGE_MAP(CCollectionFileDetailsSheet, CListViewWalkerPropertySheet)
+ ON_WM_DESTROY()
+ ON_MESSAGE(UM_DATA_CHANGED, OnDataChanged)
+END_MESSAGE_MAP()
+
+CCollectionFileDetailsSheet::CCollectionFileDetailsSheet(CTypedPtrList& aFiles, UINT uPshInvokePage, CListCtrlItemWalk* pListCtrl)
+ : CListViewWalkerPropertySheet(pListCtrl)
+{
+ m_uPshInvokePage = uPshInvokePage;
+ POSITION pos = aFiles.GetHeadPosition();
+ while (pos)
+ m_aItems.Add(aFiles.GetNext(pos));
+ m_psh.dwFlags &= ~PSH_HASHELP;
+
+ m_wndMetaData.m_psp.dwFlags &= ~PSP_HASHELP;
+ m_wndMetaData.m_psp.dwFlags |= PSP_USEICONID;
+ m_wndMetaData.m_psp.pszIcon = _T("METADATA");
+ if (m_aItems.GetSize() == 1 && thePrefs.IsExtControlsEnabled()) {
+ m_wndMetaData.SetFiles(&m_aItems);
+ AddPage(&m_wndMetaData);
+ }
+
+ LPCTSTR pPshStartPage = m_pPshStartPage;
+ if (m_uPshInvokePage != 0)
+ pPshStartPage = MAKEINTRESOURCE(m_uPshInvokePage);
+ for (int i = 0; i < m_pages.GetSize(); i++)
+ {
+ CPropertyPage* pPage = GetPage(i);
+ if (pPage->m_psp.pszTemplate == pPshStartPage)
+ {
+ m_psh.nStartPage = i;
+ break;
+ }
+ }
+}
+
+CCollectionFileDetailsSheet::~CCollectionFileDetailsSheet()
+{
+}
+
+void CCollectionFileDetailsSheet::OnDestroy()
+{
+ if (m_uPshInvokePage == 0)
+ m_pPshStartPage = GetPage(GetActiveIndex())->m_psp.pszTemplate;
+ CListViewWalkerPropertySheet::OnDestroy();
+}
+
+BOOL CCollectionFileDetailsSheet::OnInitDialog()
+{
+ EnableStackedTabs(FALSE);
+ BOOL bResult = CListViewWalkerPropertySheet::OnInitDialog();
+ HighColorTab::UpdateImageList(*this);
+ InitWindowStyles(this);
+ EnableSaveRestore(_T("CollectionFileDetailsSheet")); // call this after(!) OnInitDialog
+ UpdateTitle();
+ return bResult;
+}
+
+LRESULT CCollectionFileDetailsSheet::OnDataChanged(WPARAM, LPARAM)
+{
+ UpdateTitle();
+ return 1;
+}
+
+void CCollectionFileDetailsSheet::UpdateTitle()
+{
+ if (m_aItems.GetSize() == 1)
+ SetWindowText(GetResString(IDS_DETAILS) + _T(": ") + STATIC_DOWNCAST(CAbstractFile, m_aItems[0])->GetFileName());
+ else
+ SetWindowText(GetResString(IDS_DETAILS));
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// CCollectionListCtrl
+
+enum ECols
+{
+ colName = 0,
+ colSize,
+ colHash
+};
+
+IMPLEMENT_DYNAMIC(CCollectionListCtrl, CMuleListCtrl)
+
+BEGIN_MESSAGE_MAP(CCollectionListCtrl, CMuleListCtrl)
+ ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnClick)
+ ON_NOTIFY_REFLECT(NM_RCLICK, OnNmRClick)
+END_MESSAGE_MAP()
+
+CCollectionListCtrl::CCollectionListCtrl()
+ : CListCtrlItemWalk(this)
+{
+}
+
+CCollectionListCtrl::~CCollectionListCtrl()
+{
+}
+
+void CCollectionListCtrl::Init(CString strNameAdd)
+{
+ SetPrefsKey(_T("CollectionListCtrl") + strNameAdd);
+
+ ASSERT( GetStyle() & LVS_SHAREIMAGELISTS );
+ SendMessage(LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)theApp.GetSystemImageList());
+
+ ASSERT( (GetStyle() & LVS_SINGLESEL) == 0 );
+ SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+
+ InsertColumn(colName, GetResString(IDS_DL_FILENAME), LVCFMT_LEFT, DFLT_FILENAME_COL_WIDTH);
+ InsertColumn(colSize, GetResString(IDS_DL_SIZE), LVCFMT_RIGHT, DFLT_SIZE_COL_WIDTH);
+ InsertColumn(colHash, GetResString(IDS_FILEHASH), LVCFMT_LEFT, DFLT_HASH_COL_WIDTH);
+
+ LoadSettings();
+ SetSortArrow();
+ SortItems(SortProc, MAKELONG(GetSortItem(), (GetSortAscending() ? 0 : 1)));
+}
+
+void CCollectionListCtrl::OnLvnColumnClick(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ NMLISTVIEW *pNMListView = (NMLISTVIEW *)pNMHDR;
+
+ // Determine ascending based on whether already sorted on this column
+ int iSortItem = GetSortItem();
+ bool bOldSortAscending = GetSortAscending();
+ bool bSortAscending = (iSortItem != pNMListView->iSubItem) ? true : !bOldSortAscending;
+
+ // Item is column clicked
+ iSortItem = pNMListView->iSubItem;
+
+ // Sort table
+ UpdateSortHistory(MAKELONG(iSortItem, (bSortAscending ? 0 : 0x0001)));
+ SetSortArrow(iSortItem, bSortAscending);
+ SortItems(SortProc, MAKELONG(iSortItem, (bSortAscending ? 0 : 0x0001)));
+
+ *pResult = 0;
+}
+
+int CCollectionListCtrl::SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ const CAbstractFile *item1 = (CAbstractFile *)lParam1;
+ const CAbstractFile *item2 = (CAbstractFile *)lParam2;
+ if (item1 == NULL || item2 == NULL)
+ return 0;
+
+ int iResult;
+ switch (LOWORD(lParamSort))
+ {
+ case colName:
+ iResult = CompareLocaleStringNoCase(item1->GetFileName(),item2->GetFileName());
+ break;
+
+ case colSize:
+ iResult = CompareUnsigned64(item1->GetFileSize(), item2->GetFileSize());
+ break;
+
+ case colHash:
+ iResult = memcmp(item1->GetFileHash(), item2->GetFileHash(), 16);
+ break;
+
+ default:
+ return 0;
+ }
+ if (HIWORD(lParamSort))
+ iResult = -iResult;
+ return iResult;
+}
+
+void CCollectionListCtrl::OnNmRClick(NMHDR* /*pNMHDR*/, LRESULT* pResult)
+{
+ CTypedPtrList abstractFileList;
+ POSITION pos = GetFirstSelectedItemPosition();
+ while (pos != NULL)
+ {
+ int index = GetNextSelectedItem(pos);
+ if (index >= 0)
+ abstractFileList.AddTail((CAbstractFile*)GetItemData(index));
+ }
+
+ if(abstractFileList.GetCount() > 0)
+ {
+ CCollectionFileDetailsSheet dialog(abstractFileList, 0, this);
+ dialog.DoModal();
+ }
+ *pResult = 0;
+}
+
+void CCollectionListCtrl::AddFileToList(CAbstractFile* pAbstractFile)
+{
+ LVFINDINFO find;
+ find.flags = LVFI_PARAM;
+ find.lParam = (LPARAM)pAbstractFile;
+ int iItem = FindItem(&find);
+ if (iItem != -1)
+ {
+ ASSERT(0);
+ return;
+ }
+
+ int iImage = theApp.GetFileTypeSystemImageIdx(pAbstractFile->GetFileName());
+ iItem = InsertItem(LVIF_TEXT | LVIF_PARAM | (iImage > 0 ? LVIF_IMAGE : 0), GetItemCount(), NULL, 0, 0, iImage, (LPARAM)pAbstractFile);
+ if (iItem != -1)
+ {
+ SetItemText(iItem,colName,pAbstractFile->GetFileName());
+ SetItemText(iItem,colSize,CastItoXBytes(pAbstractFile->GetFileSize()));
+ SetItemText(iItem,colHash,::md4str(pAbstractFile->GetFileHash()));
+ }
+}
+
+void CCollectionListCtrl::RemoveFileFromList(CAbstractFile* pAbstractFile)
+{
+ LVFINDINFO find;
+ find.flags = LVFI_PARAM;
+ find.lParam = (LPARAM)pAbstractFile;
+ int iItem = FindItem(&find);
+ if (iItem != -1)
+ DeleteItem(iItem);
+ else
+ ASSERT(0);
+}
diff --git a/CollectionListCtrl.h b/CollectionListCtrl.h
new file mode 100644
index 00000000..2636723c
--- /dev/null
+++ b/CollectionListCtrl.h
@@ -0,0 +1,46 @@
+//this file is part of eMule
+//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#pragma once
+#include "MuleListCtrl.h"
+#include "ListCtrlItemWalk.h"
+
+class CAbstractFile;
+
+class CCollectionListCtrl : public CMuleListCtrl, public CListCtrlItemWalk
+{
+ DECLARE_DYNAMIC(CCollectionListCtrl)
+
+public:
+ CCollectionListCtrl();
+ virtual ~CCollectionListCtrl();
+
+ void Init(CString strNameAdd);
+ void Localize();
+
+ void AddFileToList(CAbstractFile* pAbstractFile);
+ void RemoveFileFromList(CAbstractFile* pAbstractFile);
+
+protected:
+ static int CALLBACK SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+
+ void SetAllIcons();
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnLvnColumnClick(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnNmRClick(NMHDR *pNMHDR, LRESULT *pResult);
+};
diff --git a/CollectionViewDialog.cpp b/CollectionViewDialog.cpp
new file mode 100644
index 00000000..f8a6ccf8
--- /dev/null
+++ b/CollectionViewDialog.cpp
@@ -0,0 +1,210 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "emuledlg.h"
+#include "CollectionViewDialog.h"
+#include "OtherFunctions.h"
+#include "Collection.h"
+#include "CollectionFile.h"
+#include "DownloadQueue.h"
+#include "TransferDlg.h"
+#include "CatDialog.h"
+#include "SearchDlg.h"
+#include "Partfile.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define PREF_INI_SECTION _T("CollectionViewDlg")
+
+enum ECols
+{
+ colName = 0,
+ colSize,
+ colHash
+};
+
+IMPLEMENT_DYNAMIC(CCollectionViewDialog, CDialog)
+
+BEGIN_MESSAGE_MAP(CCollectionViewDialog, CResizableDialog)
+ ON_BN_CLICKED(IDC_VCOLL_CLOSE, OnBnClickedOk)
+ ON_BN_CLICKED(IDC_VIEWCOLLECTIONDL, OnBnClickedViewCollection)
+ ON_NOTIFY(NM_DBLCLK, IDC_COLLECTIONVEWLIST, OnNmDblClkCollectionList)
+END_MESSAGE_MAP()
+
+CCollectionViewDialog::CCollectionViewDialog(CWnd* pParent /*=NULL*/)
+ : CResizableDialog(CCollectionViewDialog::IDD, pParent)
+ , m_pCollection(NULL)
+{
+ m_icoWnd = NULL;
+ m_icoColl = NULL;
+}
+
+CCollectionViewDialog::~CCollectionViewDialog()
+{
+ if (m_icoWnd)
+ VERIFY( DestroyIcon(m_icoWnd) );
+ if (m_icoColl)
+ VERIFY( DestroyIcon(m_icoColl) );
+}
+
+void CCollectionViewDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_COLLECTIONVEWLIST, m_CollectionViewList);
+ DDX_Control(pDX, IDC_COLLECTIONVIEWCATEGORYCHECK, m_AddNewCatagory);
+ DDX_Control(pDX, IDC_COLLECTIONVIEWLISTLABEL, m_CollectionViewListLabel);
+ DDX_Control(pDX, IDC_COLLECTIONVIEWLISTICON, m_CollectionViewListIcon);
+ DDX_Control(pDX, IDC_VIEWCOLLECTIONDL, m_CollectionDownload);
+ DDX_Control(pDX, IDC_VCOLL_CLOSE, m_CollectionExit);
+ DDX_Control(pDX, IDC_COLLECTIONVIEWAUTHOR, m_CollectionViewAuthor);
+ DDX_Control(pDX, IDC_COLLECTIONVIEWAUTHORKEY, m_CollectionViewAuthorKey);
+}
+
+void CCollectionViewDialog::SetCollection(CCollection* pCollection)
+{
+ if (!pCollection) {
+ ASSERT(0);
+ return;
+ }
+ m_pCollection = pCollection;
+}
+
+BOOL CCollectionViewDialog::OnInitDialog(void)
+{
+ CDialog::OnInitDialog();
+ InitWindowStyles(this);
+
+ if (!m_pCollection) {
+ ASSERT(0);
+ return TRUE;
+ }
+
+ m_CollectionViewList.Init(_T("CollectionView"));
+ SetIcon(m_icoWnd = theApp.LoadIcon(_T("Collection_View")), FALSE);
+
+ m_AddNewCatagory.SetCheck(false);
+
+ SetWindowText(GetResString(IDS_VIEWCOLLECTION) + _T(": ") + m_pCollection->m_sCollectionName);
+
+ m_CollectionViewListIcon.SetIcon(m_icoColl = theApp.LoadIcon(_T("AABCollectionFileType")));
+ m_CollectionDownload.SetWindowText(GetResString(IDS_DOWNLOAD));
+ m_CollectionExit.SetWindowText(GetResString(IDS_CW_CLOSE));
+ SetDlgItemText(IDC_COLLECTIONVIEWAUTHORLABEL, GetResString(IDS_AUTHOR) + _T(":"));
+ SetDlgItemText(IDC_COLLECTIONVIEWAUTHORKEYLABEL, GetResString(IDS_AUTHORKEY) + _T(":"));
+ SetDlgItemText(IDC_COLLECTIONVIEWCATEGORYCHECK, GetResString(IDS_COLL_ADDINCAT));
+ SetDlgItemText(IDC_VCOLL_DETAILS, GetResString(IDS_DETAILS));
+ SetDlgItemText(IDC_VCOLL_OPTIONS, GetResString(IDS_OPTIONS));
+
+ m_CollectionViewAuthor.SetWindowText(m_pCollection->m_sCollectionAuthorName);
+ m_CollectionViewAuthorKey.SetWindowText(m_pCollection->GetAuthorKeyHashString());
+
+ AddAnchor(IDC_COLLECTIONVEWLIST, TOP_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_VCOLL_DETAILS, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_VCOLL_OPTIONS, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_COLLECTIONVIEWAUTHORLABEL, BOTTOM_LEFT);
+ AddAnchor(IDC_COLLECTIONVIEWAUTHORKEYLABEL, BOTTOM_LEFT);
+ AddAnchor(IDC_COLLECTIONVIEWCATEGORYCHECK, BOTTOM_LEFT);
+ AddAnchor(IDC_COLLECTIONVIEWAUTHOR, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_COLLECTIONVIEWAUTHORKEY, BOTTOM_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_VCOLL_CLOSE, BOTTOM_RIGHT);
+ AddAnchor(IDC_VIEWCOLLECTIONDL, BOTTOM_RIGHT);
+ EnableSaveRestore(PREF_INI_SECTION);
+
+ POSITION pos = m_pCollection->m_CollectionFilesMap.GetStartPosition();
+ while (pos != NULL)
+ {
+ CCollectionFile* pCollectionFile;
+ CSKey key;
+ m_pCollection->m_CollectionFilesMap.GetNextAssoc(pos, key, pCollectionFile);
+
+ int iImage = theApp.GetFileTypeSystemImageIdx(pCollectionFile->GetFileName());
+ int iItem = m_CollectionViewList.InsertItem(LVIF_TEXT | LVIF_PARAM | (iImage > 0 ? LVIF_IMAGE : 0), m_CollectionViewList.GetItemCount(), NULL, 0, 0, iImage, (LPARAM)pCollectionFile);
+ if (iItem != -1)
+ {
+ m_CollectionViewList.SetItemText(iItem, colName, pCollectionFile->GetFileName());
+ m_CollectionViewList.SetItemText(iItem, colSize, CastItoXBytes(pCollectionFile->GetFileSize()));
+ m_CollectionViewList.SetItemText(iItem, colHash, md4str(pCollectionFile->GetFileHash()));
+ }
+ }
+
+ int iItem = m_CollectionViewList.GetItemCount();
+ while (iItem)
+ m_CollectionViewList.SetItemState(--iItem, LVIS_SELECTED, LVIS_SELECTED);
+
+ CString strTitle;
+ strTitle.Format(GetResString(IDS_COLLECTIONLIST) + _T(" (%u)"), m_CollectionViewList.GetItemCount());
+ m_CollectionViewListLabel.SetWindowText(strTitle);
+
+ return TRUE;
+}
+
+void CCollectionViewDialog::OnNmDblClkCollectionList(NMHDR* /*pNMHDR*/, LRESULT* pResult)
+{
+ DownloadSelected();
+ *pResult = 0;
+}
+
+void CCollectionViewDialog::DownloadSelected(void)
+{
+ int iNewIndex = 0;
+ for (int iIndex = 1; iIndex < thePrefs.GetCatCount(); iIndex++)
+ {
+ if (!m_pCollection->m_sCollectionName.CompareNoCase(thePrefs.GetCategory(iIndex)->strTitle))
+ {
+ iNewIndex = iIndex;
+ break;
+ }
+ }
+
+ if (m_AddNewCatagory.GetCheck() && !iNewIndex)
+ {
+ iNewIndex = theApp.emuledlg->transferwnd->AddCategory(m_pCollection->m_sCollectionName, thePrefs.GetMuleDirectory(EMULE_INCOMINGDIR), _T(""), _T(""), true);
+ theApp.emuledlg->searchwnd->UpdateCatTabs();
+ }
+
+ CTypedPtrList collectionFileList;
+ POSITION pos = m_CollectionViewList.GetFirstSelectedItemPosition();
+ while (pos != NULL)
+ {
+ int index = m_CollectionViewList.GetNextSelectedItem(pos);
+ if (index >= 0)
+ collectionFileList.AddTail((CCollectionFile*)m_CollectionViewList.GetItemData(index));
+ }
+
+ while (collectionFileList.GetCount() > 0)
+ {
+ CCollectionFile* pCollectionFile = collectionFileList.RemoveHead();
+ if (pCollectionFile)
+ theApp.downloadqueue->AddSearchToDownload(pCollectionFile->GetED2kLink(), thePrefs.AddNewFilesPaused(), iNewIndex);
+ }
+}
+
+void CCollectionViewDialog::OnBnClickedViewCollection()
+{
+ DownloadSelected();
+ OnBnClickedOk();
+}
+
+void CCollectionViewDialog::OnBnClickedOk()
+{
+ OnOK();
+}
diff --git a/CollectionViewDialog.h b/CollectionViewDialog.h
new file mode 100644
index 00000000..a9adc074
--- /dev/null
+++ b/CollectionViewDialog.h
@@ -0,0 +1,58 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "CollectionListCtrl.h"
+#include "ResizableLib\ResizableDialog.h"
+
+class CCollection;
+
+class CCollectionViewDialog : public CResizableDialog
+{
+ DECLARE_DYNAMIC(CCollectionViewDialog)
+
+public:
+ CCollectionViewDialog(CWnd* pParent = NULL); // standard constructor
+ virtual ~CCollectionViewDialog();
+
+ // Dialog Data
+ enum { IDD = IDD_COLLECTIONVIEWDIALOG };
+
+ void SetCollection(CCollection* pCollection);
+
+protected:
+ CButton m_AddNewCatagory;
+ CStatic m_CollectionViewListLabel;
+ CStatic m_CollectionViewListIcon;
+ CButton m_CollectionDownload;
+ CButton m_CollectionExit;
+ CEdit m_CollectionViewAuthor;
+ CEdit m_CollectionViewAuthorKey;
+ CCollectionListCtrl m_CollectionViewList;
+ CCollection* m_pCollection;
+ HICON m_icoWnd;
+ HICON m_icoColl;
+
+ void DownloadSelected(void);
+
+ virtual BOOL OnInitDialog(void);
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnBnClickedOk();
+ afx_msg void OnBnClickedViewCollection();
+ afx_msg void OnNmDblClkCollectionList(NMHDR *pNMHDR, LRESULT *pResult);
+};
diff --git a/ColorButton.cpp b/ColorButton.cpp
new file mode 100644
index 00000000..483ea39f
--- /dev/null
+++ b/ColorButton.cpp
@@ -0,0 +1,448 @@
+//***************************************************************************
+//
+// AUTHOR: James White (feel free to remove or otherwise mangle any part)
+//
+//***************************************************************************
+#include "stdafx.h"
+#include "ColorButton.h"
+#include "UserMsgs.h"
+
+//***********************************************************************
+//** MFC Debug Symbols **
+//***********************************************************************
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+//***********************************************************************
+//** DDX Method **
+//***********************************************************************
+
+void AFXAPI DDX_ColorButton(CDataExchange *pDX, int nIDC, COLORREF& crColour)
+{
+ HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
+ ASSERT (hWndCtrl != NULL);
+
+ CColorButton* pColourButton = (CColorButton*) CWnd::FromHandle(hWndCtrl);
+ if (pDX->m_bSaveAndValidate)
+ {
+ crColour = pColourButton->Color;
+ }
+ else // initializing
+ {
+ pColourButton->Color = crColour;
+ }
+}
+
+//***********************************************************************
+//** Constants **
+//***********************************************************************
+const int g_ciArrowSizeX = 4 ;
+const int g_ciArrowSizeY = 2 ;
+
+//***********************************************************************
+//** MFC Macros **
+//***********************************************************************
+IMPLEMENT_DYNCREATE(CColorButton, CButton)
+
+//***********************************************************************
+// Method: CColorButton::CColorButton(void)
+// Notes: Default Constructor.
+//***********************************************************************
+CColorButton::CColorButton(void):
+ _Inherited(),
+ m_Color(CLR_DEFAULT),
+ m_DefaultColor(::GetSysColor(COLOR_APPWORKSPACE)),
+ m_strDefaultText(_T("Automatic")),
+ m_strCustomText(_T("More Colors...")),
+ m_bPopupActive(FALSE),
+ m_bTrackSelection(FALSE)
+{
+}
+
+//***********************************************************************
+// Method: CColorButton::~CColorButton(void)
+// Notes: Destructor.
+//***********************************************************************
+CColorButton::~CColorButton(void)
+{
+}
+
+//***********************************************************************
+// Method: CColorButton::GetColor()
+// Notes: None.
+//***********************************************************************
+COLORREF CColorButton::GetColor(void) const
+{
+ return m_Color;
+}
+
+
+//***********************************************************************
+// Method: CColorButton::SetColor()
+// Notes: None.
+//***********************************************************************
+void CColorButton::SetColor(COLORREF Color)
+{
+ m_Color = Color;
+
+ if (::IsWindow(m_hWnd))
+ RedrawWindow();
+}
+
+
+//***********************************************************************
+// Method: CColorButton::GetDefaultColor()
+// Notes: None.
+//***********************************************************************
+COLORREF CColorButton::GetDefaultColor(void) const
+{
+ return m_DefaultColor;
+}
+
+//***********************************************************************
+// Method: CColorButton::SetDefaultColor()
+// Notes: None.
+//***********************************************************************
+void CColorButton::SetDefaultColor(COLORREF Color)
+{
+ m_DefaultColor = Color;
+}
+
+//***********************************************************************
+// Method: CColorButton::SetCustomText()
+// Notes: None.
+//***********************************************************************
+void CColorButton::SetCustomText(LPCTSTR tszText)
+{
+ m_strCustomText = tszText;
+}
+
+//***********************************************************************
+// Method: CColorButton::SetDefaultText()
+// Notes: None.
+//***********************************************************************
+void CColorButton::SetDefaultText(LPCTSTR tszText)
+{
+ m_strDefaultText = tszText;
+}
+
+
+//***********************************************************************
+// Method: CColorButton::SetTrackSelection()
+// Notes: None.
+//***********************************************************************
+void CColorButton::SetTrackSelection(BOOL bTrack)
+{
+ m_bTrackSelection = bTrack;
+}
+
+//***********************************************************************
+// Method: CColorButton::GetTrackSelection()
+// Notes: None.
+//***********************************************************************
+BOOL CColorButton::GetTrackSelection(void) const
+{
+ return m_bTrackSelection;
+}
+
+//***********************************************************************
+//** CButton Overrides **
+//***********************************************************************
+void CColorButton::PreSubclassWindow()
+{
+ ModifyStyle(0, BS_OWNERDRAW);
+
+ _Inherited::PreSubclassWindow();
+}
+
+//***********************************************************************
+//** Message Handlers **
+//***********************************************************************
+BEGIN_MESSAGE_MAP(CColorButton, CButton)
+ //{{AFX_MSG_MAP(CColorButton)
+ ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
+ ON_WM_CREATE()
+ //}}AFX_MSG_MAP
+ ON_MESSAGE(UM_CPN_SELENDOK, OnSelEndOK)
+ ON_MESSAGE(UM_CPN_SELENDCANCEL, OnSelEndCancel)
+ ON_MESSAGE(UM_CPN_SELCHANGE, OnSelChange)
+END_MESSAGE_MAP()
+
+
+//***********************************************************************
+// Method: CColorButton::OnSelEndOK()
+// Notes: None.
+//***********************************************************************
+LONG CColorButton::OnSelEndOK(UINT lParam, LONG /*wParam*/)
+{
+ m_bPopupActive = FALSE;
+
+ COLORREF OldColor = m_Color;
+
+ Color = (COLORREF)lParam;
+
+ CWnd *pParent = GetParent();
+
+ if (pParent)
+ {
+ pParent->SendMessage(UM_CPN_CLOSEUP, lParam, (WPARAM) GetDlgCtrlID());
+ pParent->SendMessage(UM_CPN_SELENDOK, lParam, (WPARAM) GetDlgCtrlID());
+ }
+
+ if (OldColor != m_Color)
+ if (pParent) pParent->SendMessage(UM_CPN_SELCHANGE, (m_Color!=CLR_DEFAULT)? m_Color:m_DefaultColor, (WPARAM) GetDlgCtrlID());
+
+ return TRUE;
+}
+
+
+//***********************************************************************
+// Method: CColorButton::OnSelEndCancel()
+// Notes: None.
+//***********************************************************************
+LONG CColorButton::OnSelEndCancel(UINT lParam, LONG /*wParam*/)
+{
+ m_bPopupActive = FALSE;
+
+ Color = (COLORREF)lParam;
+
+ CWnd *pParent = GetParent();
+
+ if (pParent)
+ {
+ pParent->SendMessage(UM_CPN_CLOSEUP, lParam, (WPARAM) GetDlgCtrlID());
+ pParent->SendMessage(UM_CPN_SELENDCANCEL, lParam, (WPARAM) GetDlgCtrlID());
+ }
+
+ return TRUE;
+}
+
+
+//***********************************************************************
+// Method: CColorButton::OnSelChange()
+// Notes: None.
+//***********************************************************************
+LONG CColorButton::OnSelChange(UINT lParam, LONG /*wParam*/)
+{
+ if (m_bTrackSelection)
+ Color = (COLORREF)lParam;
+
+ CWnd *pParent = GetParent();
+
+ if (pParent) pParent->SendMessage(UM_CPN_SELCHANGE, (m_Color!=CLR_DEFAULT)? m_Color:m_DefaultColor, (WPARAM) GetDlgCtrlID()); //Cax2 defaultcol fix
+
+ return TRUE;
+}
+
+//***********************************************************************
+// Method: CColorButton::OnCreate()
+// Notes: None.
+//***********************************************************************
+int CColorButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ if (CButton::OnCreate(lpCreateStruct) == -1)
+ return -1;
+
+ return 0;
+}
+
+//***********************************************************************
+// Method: CColorButton::OnClicked()
+// Notes: None.
+//***********************************************************************
+BOOL CColorButton::OnClicked()
+{
+ m_bPopupActive = TRUE;
+
+ CRect rDraw;
+ GetWindowRect(rDraw);
+
+ new CColourPopup(CPoint(rDraw.left, rDraw.bottom), // Point to display popup
+ m_Color, // Selected colour
+ this, // parent
+ m_strDefaultText, // "Default" text area
+ m_strCustomText); // Custom Text
+
+ CWnd *pParent = GetParent();
+
+ if (pParent)
+ pParent->SendMessage(UM_CPN_DROPDOWN, (LPARAM)m_Color, (WPARAM) GetDlgCtrlID());
+
+ return TRUE;
+}
+
+
+
+//***********************************************************************
+// Method: CColorButton::DrawItem()
+// Notes: None.
+//***********************************************************************
+void CColorButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
+{
+ ASSERT(lpDrawItemStruct);
+
+ CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
+ UINT state = lpDrawItemStruct->itemState;
+ CRect rDraw = lpDrawItemStruct->rcItem;
+ CRect rArrow;
+
+ if (m_bPopupActive)
+ state |= ODS_SELECTED|ODS_FOCUS;
+
+ //******************************************************
+ //** Draw Outer Edge
+ //******************************************************
+ UINT uFrameState = DFCS_BUTTONPUSH|DFCS_ADJUSTRECT;
+
+ if (state & ODS_SELECTED)
+ uFrameState |= DFCS_PUSHED;
+
+ if (state & ODS_DISABLED)
+ uFrameState |= DFCS_INACTIVE;
+
+ pDC->DrawFrameControl(&rDraw,
+ DFC_BUTTON,
+ uFrameState);
+
+
+ if (state & ODS_SELECTED)
+ rDraw.OffsetRect(1,1);
+
+ //******************************************************
+ //** Draw Focus
+ //******************************************************
+ if (state & ODS_FOCUS)
+ {
+ RECT rFocus = {rDraw.left,
+ rDraw.top,
+ rDraw.right - 1,
+ rDraw.bottom};
+
+ pDC->DrawFocusRect(&rFocus);
+ }
+
+ rDraw.DeflateRect(::GetSystemMetrics(SM_CXEDGE),
+ ::GetSystemMetrics(SM_CYEDGE));
+
+ //******************************************************
+ //** Draw Arrow
+ //******************************************************
+ rArrow.left = rDraw.right - g_ciArrowSizeX - ::GetSystemMetrics(SM_CXEDGE) /2;
+ rArrow.right = rArrow.left + g_ciArrowSizeX;
+ rArrow.top = (rDraw.bottom + rDraw.top)/2 - g_ciArrowSizeY / 2;
+ rArrow.bottom = (rDraw.bottom + rDraw.top)/2 + g_ciArrowSizeY / 2;
+
+ DrawArrow(pDC,
+ &rArrow,
+ 0,
+ (state & ODS_DISABLED)
+ ? ::GetSysColor(COLOR_GRAYTEXT)
+ : RGB(0,0,0));
+
+
+ rDraw.right = rArrow.left - ::GetSystemMetrics(SM_CXEDGE)/2;
+
+ //******************************************************
+ //** Draw Separator
+ //******************************************************
+ pDC->DrawEdge(&rDraw,
+ EDGE_ETCHED,
+ BF_RIGHT);
+
+ rDraw.right -= (::GetSystemMetrics(SM_CXEDGE) * 2) + 1 ;
+
+ //******************************************************
+ //** Draw Color
+ //******************************************************
+ if ((state & ODS_DISABLED) == 0)
+ {
+ pDC->FillSolidRect(&rDraw,
+ (m_Color == CLR_DEFAULT)
+ ? m_DefaultColor
+ : m_Color);
+
+ ::FrameRect(pDC->m_hDC,
+ &rDraw,
+ (HBRUSH)::GetStockObject(BLACK_BRUSH));
+ }
+}
+
+
+//***********************************************************************
+//** Static Methods **
+//***********************************************************************
+
+//***********************************************************************
+// Method: CColorButton::DrawArrow()
+// Notes: None.
+//***********************************************************************
+void CColorButton::DrawArrow(CDC* pDC,
+ RECT* pRect,
+ int iDirection,
+ COLORREF clrArrow /*= RGB(0,0,0)*/)
+{
+ POINT ptsArrow[3];
+
+ switch (iDirection)
+ {
+ case 0 : // Down
+ {
+ ptsArrow[0].x = pRect->left;
+ ptsArrow[0].y = pRect->top;
+ ptsArrow[1].x = pRect->right;
+ ptsArrow[1].y = pRect->top;
+ ptsArrow[2].x = (pRect->left + pRect->right)/2;
+ ptsArrow[2].y = pRect->bottom;
+ break;
+ }
+
+ case 1 : // Up
+ {
+ ptsArrow[0].x = pRect->left;
+ ptsArrow[0].y = pRect->bottom;
+ ptsArrow[1].x = pRect->right;
+ ptsArrow[1].y = pRect->bottom;
+ ptsArrow[2].x = (pRect->left + pRect->right)/2;
+ ptsArrow[2].y = pRect->top;
+ break;
+ }
+
+ case 2 : // Left
+ {
+ ptsArrow[0].x = pRect->right;
+ ptsArrow[0].y = pRect->top;
+ ptsArrow[1].x = pRect->right;
+ ptsArrow[1].y = pRect->bottom;
+ ptsArrow[2].x = pRect->left;
+ ptsArrow[2].y = (pRect->top + pRect->bottom)/2;
+ break;
+ }
+
+ case 3 : // Right
+ {
+ ptsArrow[0].x = pRect->left;
+ ptsArrow[0].y = pRect->top;
+ ptsArrow[1].x = pRect->left;
+ ptsArrow[1].y = pRect->bottom;
+ ptsArrow[2].x = pRect->right;
+ ptsArrow[2].y = (pRect->top + pRect->bottom)/2;
+ break;
+ }
+ }
+
+ CBrush brsArrow(clrArrow);
+ CPen penArrow(PS_SOLID, 1 , clrArrow);
+
+ CBrush* pOldBrush = pDC->SelectObject(&brsArrow);
+ CPen* pOldPen = pDC->SelectObject(&penArrow);
+
+ pDC->SetPolyFillMode(WINDING);
+ pDC->Polygon(ptsArrow, 3);
+
+ pDC->SelectObject(pOldBrush);
+ pDC->SelectObject(pOldPen);
+}
\ No newline at end of file
diff --git a/ColorButton.h b/ColorButton.h
new file mode 100644
index 00000000..997a0c37
--- /dev/null
+++ b/ColorButton.h
@@ -0,0 +1,178 @@
+//***************************************************************************
+//
+// AUTHOR: James White (feel free to remove or otherwise mangle any part)
+//
+// DESCRIPTION: This class is alarmingly similar to the CColourPicker control
+// created by Chris Maunder of www.codeproject.com. It is so as it was blatantly
+// copied from that class and is entirely dependant on his other great work
+// in CColourPopup. I was hoping for (cough.. gag..) a more Microsoft look
+// and I think this is pretty close. Hope you like it.
+//
+// ORIGINAL: http://www.codeproject.com/miscctrl/colour_picker.asp
+//
+//***************************************************************************
+#pragma once
+#include "ColourPopup.h"
+
+void AFXAPI DDX_ColorButton(CDataExchange *pDX, int nIDC, COLORREF& crColour);
+
+class CColorButton : public CButton
+{
+public:
+ DECLARE_DYNCREATE(CColorButton);
+
+ //***********************************************************************
+ // Name: CColorButton
+ // Description: Default constructor.
+ // Parameters: None.
+ // Return: None.
+ // Notes: None.
+ //***********************************************************************
+ CColorButton(void);
+
+ //***********************************************************************
+ // Name: CColorButton
+ // Description: Destructor.
+ // Parameters: None.
+ // Return: None.
+ // Notes: None.
+ //***********************************************************************
+ virtual ~CColorButton(void);
+
+ //***********************************************************************
+ //** Property Accessors **
+ //***********************************************************************
+ __declspec(property(get=GetColor,put=SetColor)) COLORREF Color;
+ __declspec(property(get=GetDefaultColor,put=SetDefaultColor)) COLORREF DefaultColor;
+ __declspec(property(get=GetTrackSelection,put=SetTrackSelection)) BOOL TrackSelection;
+ __declspec(property(put=SetCustomText)) LPCTSTR CustomText;
+ __declspec(property(put=SetDefaultText)) LPCTSTR DefaultText;
+
+ //***********************************************************************
+ // Name: GetColor
+ // Description: Returns the current color selected in the control.
+ // Parameters: void
+ // Return: COLORREF
+ // Notes: None.
+ //***********************************************************************
+ COLORREF GetColor(void) const;
+
+ //***********************************************************************
+ // Name: SetColor
+ // Description: Sets the current color selected in the control.
+ // Parameters: COLORREF Color
+ // Return: None.
+ // Notes: None.
+ //***********************************************************************
+ void SetColor(COLORREF Color);
+
+
+ //***********************************************************************
+ // Name: GetDefaultColor
+ // Description: Returns the color associated with the 'default' selection.
+ // Parameters: void
+ // Return: COLORREF
+ // Notes: None.
+ //***********************************************************************
+ COLORREF GetDefaultColor(void) const;
+
+ //***********************************************************************
+ // Name: SetDefaultColor
+ // Description: Sets the color associated with the 'default' selection.
+ // The default value is COLOR_APPWORKSPACE.
+ // Parameters: COLORREF Color
+ // Return: None.
+ // Notes: None.
+ //***********************************************************************
+ void SetDefaultColor(COLORREF Color);
+
+ //***********************************************************************
+ // Name: SetCustomText
+ // Description: Sets the text to display in the 'Custom' selection of the
+ // CColourPicker control, the default text is "More Colors...".
+ // Parameters: LPCTSTR tszText
+ // Return: None.
+ // Notes: None.
+ //***********************************************************************
+ void SetCustomText(LPCTSTR tszText);
+
+ //***********************************************************************
+ // Name: SetDefaultText
+ // Description: Sets the text to display in the 'Default' selection of the
+ // CColourPicker control, the default text is "Automatic". If
+ // this value is set to "", the 'Default' selection will not
+ // be shown.
+ // Parameters: LPCTSTR tszText
+ // Return: None.
+ // Notes: None.
+ //***********************************************************************
+ void SetDefaultText(LPCTSTR tszText);
+
+ //***********************************************************************
+ // Name: SetTrackSelection
+ // Description: Turns on/off the 'Track Selection' option of the control
+ // which shows the colors during the process of selection.
+ // Parameters: BOOL bTrack
+ // Return: None.
+ // Notes: None.
+ //***********************************************************************
+ void SetTrackSelection(BOOL bTrack);
+
+ //***********************************************************************
+ // Name: GetTrackSelection
+ // Description: Returns the state of the 'Track Selection' option.
+ // Parameters: void
+ // Return: BOOL
+ // Notes: None.
+ //***********************************************************************
+ BOOL GetTrackSelection(void) const;
+
+ //{{AFX_VIRTUAL(CColorButton)
+ public:
+ virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+ protected:
+ virtual void PreSubclassWindow();
+ //}}AFX_VIRTUAL
+
+protected:
+ //{{AFX_MSG(CColorButton)
+ afx_msg BOOL OnClicked();
+ afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ //}}AFX_MSG
+ afx_msg LONG OnSelEndOK(UINT lParam, LONG wParam);
+ afx_msg LONG OnSelEndCancel(UINT lParam, LONG wParam);
+ afx_msg LONG OnSelChange(UINT lParam, LONG wParam);
+
+ //***********************************************************************
+ // Name: DrawArrow
+ // Description: None.
+ // Parameters: CDC* pDC
+ // RECT* pRect
+ // int iDirection
+ // 0 - Down
+ // 1 - Up
+ // 2 - Left
+ // 3 - Right
+ // Return: static None.
+ // Notes: None.
+ //***********************************************************************
+ static void DrawArrow(CDC* pDC,
+ RECT* pRect,
+ int iDirection = 0,
+ COLORREF clrArrow = RGB(0,0,0));
+
+
+ DECLARE_MESSAGE_MAP()
+
+ COLORREF m_Color;
+ COLORREF m_DefaultColor;
+ CString m_strDefaultText;
+ CString m_strCustomText;
+ BOOL m_bPopupActive;
+ BOOL m_bTrackSelection;
+
+private:
+
+ typedef CButton _Inherited;
+
+};
diff --git a/ColorFrameCtrl.cpp b/ColorFrameCtrl.cpp
new file mode 100644
index 00000000..807da1c5
--- /dev/null
+++ b/ColorFrameCtrl.cpp
@@ -0,0 +1,82 @@
+#include "stdafx.h"
+#include "ColorFrameCtrl.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CColorFrameCtrl
+
+BEGIN_MESSAGE_MAP(CColorFrameCtrl, CWnd)
+ ON_WM_PAINT()
+ ON_WM_SIZE()
+END_MESSAGE_MAP()
+
+CColorFrameCtrl::CColorFrameCtrl()
+{
+ m_crBackColor = RGB(0, 0, 0); // see also SetBackgroundColor
+ m_crFrameColor = RGB(0, 255, 255); // see also SetFrameColor
+
+ m_brushBack.CreateSolidBrush(m_crBackColor);
+ m_brushFrame.CreateSolidBrush(m_crFrameColor);
+}
+
+CColorFrameCtrl::~CColorFrameCtrl()
+{
+ m_brushFrame.DeleteObject();
+ m_brushBack.DeleteObject();
+}
+
+BOOL CColorFrameCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
+{
+ BOOL result;
+ static CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, AfxGetApp()->LoadStandardCursor(IDC_ARROW));
+
+ result = CWnd::CreateEx( WS_EX_STATICEDGE,
+ className, NULL, dwStyle,
+ rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
+ pParentWnd->GetSafeHwnd(), (HMENU)nID);
+ if (result != 0)
+ Invalidate();
+ return result;
+}
+
+void CColorFrameCtrl::SetFrameColor( COLORREF color )
+{
+ m_crFrameColor = color;
+ m_brushFrame.DeleteObject();
+ m_brushFrame.CreateSolidBrush(m_crFrameColor);
+
+ // clear out the existing garbage, re-start with a clean plot
+ Invalidate();
+}
+
+void CColorFrameCtrl::SetBackgroundColor(COLORREF color)
+{
+ m_crBackColor = color;
+
+ m_brushBack.DeleteObject();
+ m_brushBack.CreateSolidBrush(m_crBackColor);
+
+ // clear out the existing garbage, re-start with a clean plot
+ Invalidate();
+}
+
+void CColorFrameCtrl::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+
+ dc.FillRect(m_rectClient, &m_brushBack);
+ dc.FrameRect(m_rectClient, &m_brushFrame);
+}
+
+void CColorFrameCtrl::OnSize(UINT nType, int cx, int cy)
+{
+ // NOTE: OnSize automatically gets called during the setup of the control
+ CWnd::OnSize(nType, cx, cy);
+ GetClientRect(m_rectClient);
+}
diff --git a/ColorFrameCtrl.h b/ColorFrameCtrl.h
new file mode 100644
index 00000000..51fe5dea
--- /dev/null
+++ b/ColorFrameCtrl.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/////////////////////////////////////////////////////////////////////////////
+// CColorFrameCtrl window
+
+class CColorFrameCtrl : public CWnd
+{
+public:
+ CColorFrameCtrl();
+ virtual ~CColorFrameCtrl();
+
+ virtual BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0);
+
+ void SetFrameColor(COLORREF color);
+ void SetBackgroundColor(COLORREF color);
+
+ COLORREF m_crBackColor; // background color
+ COLORREF m_crFrameColor; // frame color
+
+protected:
+ CRect m_rectClient;
+ CBrush m_brushBack;
+ CBrush m_brushFrame;
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnPaint();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+};
diff --git a/ColourPopup.cpp b/ColourPopup.cpp
new file mode 100644
index 00000000..65fa4a86
--- /dev/null
+++ b/ColourPopup.cpp
@@ -0,0 +1,952 @@
+// ColourPopup.cpp : implementation file
+//
+// Written by Chris Maunder (chrismaunder@codeguru.com)
+// Extended by Alexander Bischofberger (bischofb@informatik.tu-muenchen.de)
+// Copyright (c) 1998.
+//
+// Updated 30 May 1998 to allow any number of colours, and to
+// make the appearance closer to Office 97.
+// Also added "Default" text area. (CJM)
+//
+// 13 June 1998 Fixed change of focus bug (CJM)
+// 30 June 1998 Fixed bug caused by focus bug fix (D'oh!!)
+// Solution suggested by Paul Wilkerson.
+//
+// ColourPopup is a helper class for the colour picker control
+// CColourPicker. Check out the header file or the accompanying
+// HTML doc file for details.
+//
+// This code may be used in compiled form in any way you desire. This
+// file may be redistributed unmodified by any means PROVIDING it is
+// not sold for profit without the authors written consent, and
+// providing that this notice and the authors name is included.
+//
+// This file is provided "as is" with no expressed or implied warranty.
+// The author accepts no liability if it causes any damage to you or your
+// computer whatsoever. It's free, so don't hassle me about it.
+//
+// Expect bugs.
+//
+// Please use and enjoy. Please let me know of any bugs/mods/improvements
+// that you have found/implemented and I will fix/incorporate them into this
+// file.
+#include "stdafx.h"
+#include
+#include "ColourPopup.h"
+#include "UserMsgs.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+#define DEFAULT_BOX_VALUE -3
+#define CUSTOM_BOX_VALUE -2
+#define INVALID_COLOUR -1
+
+#define MAX_COLOURS 100
+
+
+ColourTableEntry CColourPopup::m_crColours[] =
+ {
+ { RGB(0x00, 0x00, 0x00), _T("Black") },
+ { RGB(0xA5, 0x2A, 0x00), _T("Brown") },
+ { RGB(0x00, 0x40, 0x40), _T("Dark Olive Green") },
+ { RGB(0x00, 0x55, 0x00), _T("Dark Green") },
+ { RGB(0x00, 0x00, 0x5E), _T("Dark Teal") },
+ { RGB(0x00, 0x00, 0x8B), _T("Dark blue") },
+ { RGB(0x4B, 0x00, 0x82), _T("Indigo") },
+ { RGB(0x28, 0x28, 0x28), _T("Dark grey") },
+
+ { RGB(0x8B, 0x00, 0x00), _T("Dark red") },
+ { RGB(0xFF, 0x68, 0x20), _T("Orange") },
+ { RGB(0x8B, 0x8B, 0x00), _T("Dark yellow") },
+ { RGB(0x00, 0x93, 0x00), _T("Green") },
+ { RGB(0x38, 0x8E, 0x8E), _T("Teal") },
+ { RGB(0x00, 0x00, 0xFF), _T("Blue") },
+ { RGB(0x7B, 0x7B, 0xC0), _T("Blue-grey") },
+ { RGB(0x66, 0x66, 0x66), _T("Grey - 40") },
+
+ { RGB(0xFF, 0x00, 0x00), _T("Red") },
+ { RGB(0xFF, 0xAD, 0x5B), _T("Light orange") },
+ { RGB(0x32, 0xCD, 0x32), _T("Lime") },
+ { RGB(0x3C, 0xB3, 0x71), _T("Sea green") },
+ { RGB(0x7F, 0xFF, 0xD4), _T("Aqua") },
+ { RGB(0x7D, 0x9E, 0xC0), _T("Light blue") },
+ { RGB(0x80, 0x00, 0x80), _T("Violet") },
+ { RGB(0x7F, 0x7F, 0x7F), _T("Grey - 50") },
+
+ { RGB(0xFF, 0xC0, 0xCB), _T("Pink") },
+ { RGB(0xFF, 0xD7, 0x00), _T("Gold") },
+ { RGB(0xFF, 0xFF, 0x00), _T("Yellow") },
+ { RGB(0x00, 0xFF, 0x00), _T("Bright green") },
+ { RGB(0x40, 0xE0, 0xD0), _T("Turquoise") },
+ { RGB(0xC0, 0xFF, 0xFF), _T("Skyblue") },
+ { RGB(0x48, 0x00, 0x48), _T("Plum") },
+ { RGB(0xC0, 0xC0, 0xC0), _T("Light grey") },
+
+ { RGB(0xFF, 0xE4, 0xE1), _T("Rose") },
+ { RGB(0xD2, 0xB4, 0x8C), _T("Tan") },
+ { RGB(0xFF, 0xFF, 0xE0), _T("Light yellow") },
+ { RGB(0x98, 0xFB, 0x98), _T("Pale green ") },
+ { RGB(0xAF, 0xEE, 0xEE), _T("Pale turquoise") },
+ { RGB(0x68, 0x83, 0x8B), _T("Pale blue") },
+ { RGB(0xE6, 0xE6, 0xFA), _T("Lavender") },
+ { RGB(0xFF, 0xFF, 0xFF), _T("White") }
+ };
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup
+
+CColourPopup::CColourPopup()
+{
+ Initialise();
+}
+
+CColourPopup::CColourPopup(CPoint p, COLORREF crColour, CWnd* pParentWnd,
+ LPCTSTR szDefaultText /* = NULL */,
+ LPCTSTR szCustomText /* = NULL */,
+ COLORREF* colourArray /* = NULL*/,
+ int NumberOfColours /* = 0*/ )
+{
+ colourArrayPassed = colourArray;//copy the pointer to the array of colours we will be using
+ if(colourArray && NumberOfColours)
+ m_nNumColours = NumberOfColours;
+ else
+ colourArrayPassed = NULL; //if an array is passed without a size parameter ignore it and use the defaults
+
+ Initialise();//If not a custom palette intialise as NORMAL!
+
+ m_crColour = m_crInitialColour = crColour;
+ m_pParent = pParentWnd;
+ m_strDefaultText = (szDefaultText)? szDefaultText : _T("");
+ m_strCustomText = (szCustomText)? szCustomText : _T("");
+
+ CColourPopup::Create(p, crColour, pParentWnd, szDefaultText, szCustomText);
+}
+
+void CColourPopup::Initialise()
+{
+ //set size if it has not been set already
+ if(colourArrayPassed==NULL)
+ m_nNumColours = sizeof(m_crColours)/sizeof(ColourTableEntry);
+
+ ASSERT(m_nNumColours <= MAX_COLOURS);
+ if (m_nNumColours > MAX_COLOURS)
+ m_nNumColours = MAX_COLOURS;
+
+ m_nNumColumns = 0;
+ m_nNumRows = 0;
+ m_nBoxSize = 18;
+ m_nMargin = ::GetSystemMetrics(SM_CXEDGE);
+ m_nCurrentSel = INVALID_COLOUR;
+ m_nChosenColourSel = INVALID_COLOUR;
+ m_pParent = NULL;
+ m_crColour = m_crInitialColour = RGB(0,0,0);
+
+ m_bChildWindowVisible = FALSE;
+
+ // Idiot check: Make sure the colour square is at least 5 x 5;
+ if (m_nBoxSize - 2*m_nMargin - 2 < 5)
+ m_nBoxSize = 5 + 2*m_nMargin + 2;
+
+ // Create the font
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
+ m_Font.CreateFontIndirect(&(ncm.lfMessageFont));
+
+ // Create the palette
+ struct
+ {
+ LOGPALETTE LogPalette;
+ PALETTEENTRY PalEntry[MAX_COLOURS];
+ }
+ pal;
+
+ LOGPALETTE* pLogPalette = (LOGPALETTE*) &pal;
+ pLogPalette->palVersion = 0x300;
+ pLogPalette->palNumEntries = (WORD) m_nNumColours;
+
+ if(colourArrayPassed==NULL)
+ {//use default array
+ for (int i = 0; i < m_nNumColours; i++)
+ {
+ pLogPalette->palPalEntry[i].peRed = GetRValue(m_crColours[i].crColour);
+ pLogPalette->palPalEntry[i].peGreen = GetGValue(m_crColours[i].crColour);
+ pLogPalette->palPalEntry[i].peBlue = GetBValue(m_crColours[i].crColour);
+ pLogPalette->palPalEntry[i].peFlags = 0;
+ }
+ }
+ else
+ {//if an array has been passed use it
+ for(int i=0;ipalPalEntry[i].peRed = GetRValue(colourArrayPassed[i]);
+ pLogPalette->palPalEntry[i].peGreen = GetGValue(colourArrayPassed[i]);
+ pLogPalette->palPalEntry[i].peBlue = GetBValue(colourArrayPassed[i]);
+ pLogPalette->palPalEntry[i].peFlags = 0;
+ }
+ }
+
+ m_Palette.CreatePalette(pLogPalette);
+}
+
+CColourPopup::~CColourPopup()
+{
+ m_Font.DeleteObject();
+ m_Palette.DeleteObject();
+}
+
+BOOL CColourPopup::Create(CPoint p, COLORREF crColour, CWnd* pParentWnd,
+ LPCTSTR szDefaultText /* = NULL */,
+ LPCTSTR szCustomText /* = NULL */)
+{
+ ASSERT(pParentWnd && ::IsWindow(pParentWnd->GetSafeHwnd()));
+
+ m_pParent = pParentWnd;
+ m_crColour = m_crInitialColour = crColour;
+
+ // Get the class name and create the window
+ CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS|CS_HREDRAW|CS_VREDRAW,
+ AfxGetApp()->LoadStandardCursor(IDC_ARROW),
+ (HBRUSH) (COLOR_BTNFACE+1),
+ 0);
+
+ if (!CWnd::CreateEx(0, szClassName, _T(""), WS_VISIBLE|WS_POPUP,
+ p.x, p.y, 100, 100, // size updated soon
+ pParentWnd->GetSafeHwnd(), 0, NULL))
+ return FALSE;
+
+ // Store the Custom text
+ if (szCustomText != NULL)
+ m_strCustomText = szCustomText;
+
+ // Store the Default Area text
+ if (szDefaultText != NULL)
+ m_strDefaultText = szDefaultText;
+
+ // Set the window size
+ SetWindowSize();
+
+ // Create the tooltips
+ CreateToolTips();
+
+ // Find which cell (if any) corresponds to the initial colour
+ FindCellFromColour(crColour);
+
+ // Capture all mouse events for the life of this window
+ SetCapture();
+
+ return TRUE;
+}
+
+BEGIN_MESSAGE_MAP(CColourPopup, CWnd)
+//{{AFX_MSG_MAP(CColourPopup)
+ON_WM_NCDESTROY()
+ON_WM_LBUTTONUP()
+ON_WM_PAINT()
+ON_WM_MOUSEMOVE()
+ON_WM_KEYDOWN()
+ON_WM_QUERYNEWPALETTE()
+ON_WM_PALETTECHANGED()
+ON_WM_KILLFOCUS()
+ON_WM_ACTIVATEAPP()
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup message handlers
+
+// For tooltips
+BOOL CColourPopup::PreTranslateMessage(MSG* pMsg)
+{
+ m_ToolTip.RelayEvent(pMsg);
+
+ // Fix (Adrian Roman): Sometimes if the picker loses focus it is never destroyed
+ if (GetCapture()->GetSafeHwnd() != m_hWnd)
+ SetCapture();
+
+ return CWnd::PreTranslateMessage(pMsg);
+}
+
+// If an arrow key is pressed, then move the selection
+void CColourPopup::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
+{
+ int row = GetRow(m_nCurrentSel),
+ col = GetColumn(m_nCurrentSel);
+
+ if (nChar == VK_DOWN)
+ {
+ if (row == DEFAULT_BOX_VALUE)
+ row = col = 0;
+ else if (row == CUSTOM_BOX_VALUE)
+ {
+ if (m_strDefaultText.GetLength())
+ row = col = DEFAULT_BOX_VALUE;
+ else
+ row = col = 0;
+ }
+ else
+ {
+ row++;
+ if (GetIndex(row,col) < 0)
+ {
+ if (m_strCustomText.GetLength())
+ row = col = CUSTOM_BOX_VALUE;
+ else if (m_strDefaultText.GetLength())
+ row = col = DEFAULT_BOX_VALUE;
+ else
+ row = col = 0;
+ }
+ }
+ ChangeSelection(GetIndex(row, col));
+ }
+
+ if (nChar == VK_UP)
+ {
+ if (row == DEFAULT_BOX_VALUE)
+ {
+ if (m_strCustomText.GetLength())
+ row = col = CUSTOM_BOX_VALUE;
+ else
+ {
+ row = GetRow(m_nNumColours-1);
+ col = GetColumn(m_nNumColours-1);
+ }
+ }
+ else if (row == CUSTOM_BOX_VALUE)
+ {
+ row = GetRow(m_nNumColours-1);
+ col = GetColumn(m_nNumColours-1);
+ }
+ else if (row > 0)
+ row--;
+ else /* row == 0 */
+ {
+ if (m_strDefaultText.GetLength())
+ row = col = DEFAULT_BOX_VALUE;
+ else if (m_strCustomText.GetLength())
+ row = col = CUSTOM_BOX_VALUE;
+ else
+ {
+ row = GetRow(m_nNumColours-1);
+ col = GetColumn(m_nNumColours-1);
+ }
+ }
+ ChangeSelection(GetIndex(row, col));
+ }
+
+ if (nChar == VK_RIGHT)
+ {
+ if (row == DEFAULT_BOX_VALUE)
+ row = col = 0;
+ else if (row == CUSTOM_BOX_VALUE)
+ {
+ if (m_strDefaultText.GetLength())
+ row = col = DEFAULT_BOX_VALUE;
+ else
+ row = col = 0;
+ }
+ else if (col < m_nNumColumns-1)
+ col++;
+ else
+ {
+ col = 0;
+ row++;
+ }
+
+ if (GetIndex(row,col) == INVALID_COLOUR)
+ {
+ if (m_strCustomText.GetLength())
+ row = col = CUSTOM_BOX_VALUE;
+ else if (m_strDefaultText.GetLength())
+ row = col = DEFAULT_BOX_VALUE;
+ else
+ row = col = 0;
+ }
+
+ ChangeSelection(GetIndex(row, col));
+ }
+
+ if (nChar == VK_LEFT)
+ {
+ if (row == DEFAULT_BOX_VALUE)
+ {
+ if (m_strCustomText.GetLength())
+ row = col = CUSTOM_BOX_VALUE;
+ else
+ {
+ row = GetRow(m_nNumColours-1);
+ col = GetColumn(m_nNumColours-1);
+ }
+ }
+ else if (row == CUSTOM_BOX_VALUE)
+ {
+ row = GetRow(m_nNumColours-1);
+ col = GetColumn(m_nNumColours-1);
+ }
+ else if (col > 0)
+ col--;
+ else /* col == 0 */
+ {
+ if (row > 0)
+ {
+ row--;
+ col = m_nNumColumns-1;
+ }
+ else
+ {
+ if (m_strDefaultText.GetLength())
+ row = col = DEFAULT_BOX_VALUE;
+ else if (m_strCustomText.GetLength())
+ row = col = CUSTOM_BOX_VALUE;
+ else
+ {
+ row = GetRow(m_nNumColours-1);
+ col = GetColumn(m_nNumColours-1);
+ }
+ }
+ }
+ ChangeSelection(GetIndex(row, col));
+ }
+
+ if (nChar == VK_ESCAPE)
+ {
+ m_crColour = m_crInitialColour;
+ EndSelection(UM_CPN_SELENDCANCEL);
+ return;
+ }
+
+ if (nChar == VK_RETURN || nChar == VK_SPACE)
+ {
+ EndSelection(UM_CPN_SELENDOK);
+ return;
+ }
+
+ CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
+}
+
+// auto-deletion
+void CColourPopup::OnNcDestroy()
+{
+ CWnd::OnNcDestroy();
+ delete this;
+}
+
+void CColourPopup::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+
+ // Draw the Default Area text
+ if (m_strDefaultText.GetLength())
+ DrawCell(&dc, DEFAULT_BOX_VALUE);
+
+ // Draw colour cells
+ for (int i = 0; i < m_nNumColours; i++)
+ DrawCell(&dc, i);
+
+ // Draw custom text
+ if (m_strCustomText.GetLength())
+ DrawCell(&dc, CUSTOM_BOX_VALUE);
+
+ // Draw raised window edge (ex-window style WS_EX_WINDOWEDGE is sposed to do this,
+ // but for some reason isn't
+ CRect rect;
+ GetClientRect(rect);
+ dc.DrawEdge(rect, EDGE_RAISED, BF_RECT);
+}
+
+void CColourPopup::OnMouseMove(UINT nFlags, CPoint point)
+{
+ int nNewSelection = INVALID_COLOUR;
+
+ // Translate points to be relative raised window edge
+ point.x -= m_nMargin;
+ point.y -= m_nMargin;
+
+ // First check we aren't in text box
+ if (m_strCustomText.GetLength() && m_CustomTextRect.PtInRect(point))
+ nNewSelection = CUSTOM_BOX_VALUE;
+ else if (m_strDefaultText.GetLength() && m_DefaultTextRect.PtInRect(point))
+ nNewSelection = DEFAULT_BOX_VALUE;
+ else
+ {
+ // Take into account text box
+ if (m_strDefaultText.GetLength())
+ point.y -= m_DefaultTextRect.Height();
+
+ // Get the row and column
+ nNewSelection = GetIndex(point.y / m_nBoxSize, point.x / m_nBoxSize);
+
+ // In range? If not, default and exit
+ if (nNewSelection < 0 || nNewSelection >= m_nNumColours)
+ {
+ CWnd::OnMouseMove(nFlags, point);
+ return;
+ }
+ }
+
+ // OK - we have the row and column of the current selection (may be CUSTOM_BOX_VALUE)
+ // Has the row/col selection changed? If yes, then redraw old and new cells.
+ if (nNewSelection != m_nCurrentSel)
+ ChangeSelection(nNewSelection);
+
+ CWnd::OnMouseMove(nFlags, point);
+}
+
+// End selection on LButtonUp
+void CColourPopup::OnLButtonUp(UINT nFlags, CPoint point)
+{
+ CWnd::OnLButtonUp(nFlags, point);
+
+ DWORD pos = GetMessagePos();
+ point = CPoint(LOWORD(pos), HIWORD(pos));
+
+ if (m_WindowRect.PtInRect(point))
+ EndSelection(UM_CPN_SELENDOK);
+ else
+ EndSelection(UM_CPN_SELENDCANCEL);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup implementation
+
+int CColourPopup::GetIndex(int row, int col) const
+{
+ if ((row == CUSTOM_BOX_VALUE || col == CUSTOM_BOX_VALUE) && m_strCustomText.GetLength())
+ return CUSTOM_BOX_VALUE;
+ else if ((row == DEFAULT_BOX_VALUE || col == DEFAULT_BOX_VALUE) && m_strDefaultText.GetLength())
+ return DEFAULT_BOX_VALUE;
+ else if (row < 0 || col < 0 || row >= m_nNumRows || col >= m_nNumColumns)
+ return INVALID_COLOUR;
+ else
+ {
+ if (row*m_nNumColumns + col >= m_nNumColours)
+ return INVALID_COLOUR;
+ else
+ return row*m_nNumColumns + col;
+ }
+}
+
+int CColourPopup::GetRow(int nIndex) const
+{
+ if (nIndex == CUSTOM_BOX_VALUE && m_strCustomText.GetLength())
+ return CUSTOM_BOX_VALUE;
+ else if (nIndex == DEFAULT_BOX_VALUE && m_strDefaultText.GetLength())
+ return DEFAULT_BOX_VALUE;
+ else if (nIndex < 0 || nIndex >= m_nNumColours)
+ return INVALID_COLOUR;
+ else
+ return nIndex / m_nNumColumns;
+}
+
+int CColourPopup::GetColumn(int nIndex) const
+{
+ if (nIndex == CUSTOM_BOX_VALUE && m_strCustomText.GetLength())
+ return CUSTOM_BOX_VALUE;
+ else if (nIndex == DEFAULT_BOX_VALUE && m_strDefaultText.GetLength())
+ return DEFAULT_BOX_VALUE;
+ else if (nIndex < 0 || nIndex >= m_nNumColours)
+ return INVALID_COLOUR;
+ else
+ return nIndex % m_nNumColumns;
+}
+
+void CColourPopup::FindCellFromColour(COLORREF crColour)
+{
+ if (crColour == CLR_DEFAULT && m_strDefaultText.GetLength())
+ {
+ m_nChosenColourSel = DEFAULT_BOX_VALUE;
+ return;
+ }
+
+ for (int i = 0; i < m_nNumColours; i++)
+ {
+ if (GetColour(i) == crColour)
+ {
+ m_nChosenColourSel = i;
+ return;
+ }
+ }
+
+ if (m_strCustomText.GetLength())
+ m_nChosenColourSel = CUSTOM_BOX_VALUE;
+ else
+ m_nChosenColourSel = INVALID_COLOUR;
+}
+
+// Gets the dimensions of the colour cell given by (row,col)
+BOOL CColourPopup::GetCellRect(int nIndex, const LPRECT& rect)
+{
+ if (nIndex == CUSTOM_BOX_VALUE)
+ {
+ ::SetRect(rect,
+ m_CustomTextRect.left, m_CustomTextRect.top,
+ m_CustomTextRect.right, m_CustomTextRect.bottom);
+ return TRUE;
+ }
+ else if (nIndex == DEFAULT_BOX_VALUE)
+ {
+ ::SetRect(rect,
+ m_DefaultTextRect.left, m_DefaultTextRect.top,
+ m_DefaultTextRect.right, m_DefaultTextRect.bottom);
+ return TRUE;
+ }
+
+ if (nIndex < 0 || nIndex >= m_nNumColours)
+ return FALSE;
+
+ rect->left = GetColumn(nIndex) * m_nBoxSize + m_nMargin;
+ rect->top = GetRow(nIndex) * m_nBoxSize + m_nMargin;
+
+ // Move everything down if we are displaying a default text area
+ if (m_strDefaultText.GetLength())
+ rect->top += (m_nMargin + m_DefaultTextRect.Height());
+
+ rect->right = rect->left + m_nBoxSize;
+ rect->bottom = rect->top + m_nBoxSize;
+
+ return TRUE;
+}
+
+// Works out an appropriate size and position of this window
+void CColourPopup::SetWindowSize()
+{
+ CSize TextSize;
+
+ // If we are showing a custom or default text area, get the font and text size.
+ if (m_strCustomText.GetLength() || m_strDefaultText.GetLength())
+ {
+ CClientDC dc(this);
+ CFont* pOldFont = (CFont*) dc.SelectObject(&m_Font);
+
+ // Get the size of the custom text (if there IS custom text)
+ TextSize = CSize(0,0);
+ if (m_strCustomText.GetLength())
+ TextSize = dc.GetTextExtent(m_strCustomText);
+
+ // Get the size of the default text (if there IS default text)
+ if (m_strDefaultText.GetLength())
+ {
+ CSize DefaultSize = dc.GetTextExtent(m_strDefaultText);
+ if (DefaultSize.cx > TextSize.cx)
+ TextSize.cx = DefaultSize.cx;
+ if (DefaultSize.cy > TextSize.cy)
+ TextSize.cy = DefaultSize.cy;
+ }
+
+ dc.SelectObject(pOldFont);
+ TextSize += CSize(2*m_nMargin,2*m_nMargin);
+
+ // Add even more space to draw the horizontal line
+ TextSize.cy += 2*m_nMargin + 2;
+ }
+
+ // Get the number of columns and rows
+ //m_nNumColumns = (int) sqrt((double)m_nNumColours); // for a square window (yuk)
+ m_nNumColumns = 8;
+ m_nNumRows = m_nNumColours / m_nNumColumns;
+ if (m_nNumColours % m_nNumColumns)
+ m_nNumRows++;
+
+ // Get the current window position, and set the new size
+ CRect rect;
+ GetWindowRect(rect);
+
+ m_WindowRect.SetRect(rect.left, rect.top,
+ rect.left + m_nNumColumns*m_nBoxSize + 2*m_nMargin,
+ rect.top + m_nNumRows*m_nBoxSize + 2*m_nMargin);
+
+ // if custom text, then expand window if necessary, and set text width as
+ // window width
+ if (m_strDefaultText.GetLength())
+ {
+ if (TextSize.cx > m_WindowRect.Width())
+ m_WindowRect.right = m_WindowRect.left + TextSize.cx;
+ TextSize.cx = m_WindowRect.Width()-2*m_nMargin;
+
+ // Work out the text area
+ m_DefaultTextRect.SetRect(m_nMargin, m_nMargin,
+ m_nMargin+TextSize.cx, 2*m_nMargin+TextSize.cy);
+ m_WindowRect.bottom += m_DefaultTextRect.Height() + 2*m_nMargin;
+ }
+
+ // if custom text, then expand window if necessary, and set text width as
+ // window width
+ if (m_strCustomText.GetLength())
+ {
+ if (TextSize.cx > m_WindowRect.Width())
+ m_WindowRect.right = m_WindowRect.left + TextSize.cx;
+ TextSize.cx = m_WindowRect.Width()-2*m_nMargin;
+
+ // Work out the text area
+ m_CustomTextRect.SetRect(m_nMargin, m_WindowRect.Height(),
+ m_nMargin+TextSize.cx,
+ m_WindowRect.Height()+m_nMargin+TextSize.cy);
+ m_WindowRect.bottom += m_CustomTextRect.Height() + 2*m_nMargin;
+ }
+
+ // Need to check it'll fit on screen: Too far right?
+ CSize ScreenSize(::GetSystemMetrics(SM_CXSCREEN), ::GetSystemMetrics(SM_CYSCREEN));
+ if (m_WindowRect.right > ScreenSize.cx)
+ m_WindowRect.OffsetRect(-(m_WindowRect.right - ScreenSize.cx), 0);
+
+ // Too far left?
+ if (m_WindowRect.left < 0)
+ m_WindowRect.OffsetRect( -m_WindowRect.left, 0);
+
+ // Bottom falling out of screen?
+ if (m_WindowRect.bottom > ScreenSize.cy)
+ {
+ CRect ParentRect;
+ m_pParent->GetWindowRect(ParentRect);
+ m_WindowRect.OffsetRect(0, -(ParentRect.Height() + m_WindowRect.Height()));
+ }
+
+ // Set the window size and position
+ MoveWindow(m_WindowRect, TRUE);
+}
+
+void CColourPopup::CreateToolTips()
+{
+ // Create the tool tip
+ if (!m_ToolTip.Create(this))
+ return;
+
+ // Add a tool for each cell
+ for (int i = 0; i < m_nNumColours; i++)
+ {
+ CRect rect;
+ if (!GetCellRect(i, rect))
+ continue;
+ m_ToolTip.AddTool(this,_T(""), rect, 1); // GetColourName(i)
+ }
+}
+
+void CColourPopup::ChangeSelection(int nIndex)
+{
+ CClientDC dc(this); // device context for drawing
+
+ if (nIndex > m_nNumColours)
+ nIndex = CUSTOM_BOX_VALUE;
+
+ if ((m_nCurrentSel >= 0 && m_nCurrentSel < m_nNumColours) ||
+ m_nCurrentSel == CUSTOM_BOX_VALUE || m_nCurrentSel == DEFAULT_BOX_VALUE)
+ {
+ // Set Current selection as invalid and redraw old selection (this way
+ // the old selection will be drawn unselected)
+ int OldSel = m_nCurrentSel;
+ m_nCurrentSel = INVALID_COLOUR;
+ DrawCell(&dc, OldSel);
+ }
+
+ // Set the current selection as row/col and draw (it will be drawn selected)
+ m_nCurrentSel = nIndex;
+ DrawCell(&dc, m_nCurrentSel);
+
+ // Store the current colour
+ if (m_nCurrentSel == CUSTOM_BOX_VALUE)
+ m_pParent->SendMessage(UM_CPN_SELCHANGE, (WPARAM) m_crInitialColour, 0);
+ else if (m_nCurrentSel == DEFAULT_BOX_VALUE)
+ {
+ m_crColour = CLR_DEFAULT;
+ m_pParent->SendMessage(UM_CPN_SELCHANGE, (WPARAM) CLR_DEFAULT, 0);
+ }
+ else
+ {
+ m_crColour = GetColour(m_nCurrentSel);
+ m_pParent->SendMessage(UM_CPN_SELCHANGE, (WPARAM) m_crColour, 0);
+ }
+}
+
+void CColourPopup::EndSelection(int nMessage)
+{
+ ReleaseCapture();
+
+ // If custom text selected, perform a custom colour selection
+ if (nMessage != UM_CPN_SELENDCANCEL && m_nCurrentSel == CUSTOM_BOX_VALUE)
+ {
+ m_bChildWindowVisible = TRUE;
+
+ CColorDialog dlg(m_crInitialColour, CC_FULLOPEN | CC_ANYCOLOR, this);
+
+ if (dlg.DoModal() == IDOK)
+ m_crColour = dlg.GetColor();
+ else
+ nMessage = UM_CPN_SELENDCANCEL;
+
+ m_bChildWindowVisible = FALSE;
+ }
+
+ if (nMessage == UM_CPN_SELENDCANCEL)
+ m_crColour = m_crInitialColour;
+
+ m_pParent->SendMessage(nMessage, (WPARAM) m_crColour, 0);
+
+ // Kill focus bug fixed by Martin Wawrusch
+ if (!m_bChildWindowVisible)
+ DestroyWindow();
+}
+
+void CColourPopup::DrawCell(CDC* pDC, int nIndex)
+{
+ // For the Custom Text area
+ if (m_strCustomText.GetLength() && nIndex == CUSTOM_BOX_VALUE)
+ {
+ // The extent of the actual text button
+ CRect TextButtonRect = m_CustomTextRect;
+ TextButtonRect.top += 2*m_nMargin;
+
+ // Fill background
+ pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));
+
+ // Draw horizontal line
+ pDC->FillSolidRect(m_CustomTextRect.left+2*m_nMargin, m_CustomTextRect.top,
+ m_CustomTextRect.Width()-4*m_nMargin, 1, ::GetSysColor(COLOR_3DSHADOW));
+ pDC->FillSolidRect(m_CustomTextRect.left+2*m_nMargin, m_CustomTextRect.top+1,
+ m_CustomTextRect.Width()-4*m_nMargin, 1, ::GetSysColor(COLOR_3DHILIGHT));
+
+ TextButtonRect.DeflateRect(1,1);
+
+ // fill background
+ if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
+ pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DLIGHT));
+ else
+ pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));
+
+ // Draw button
+ if (m_nCurrentSel == nIndex)
+ pDC->DrawEdge(TextButtonRect, BDR_RAISEDINNER, BF_RECT);
+ else if (m_nChosenColourSel == nIndex)
+ pDC->DrawEdge(TextButtonRect, BDR_SUNKENOUTER, BF_RECT);
+
+ // Draw custom text
+ CFont *pOldFont = (CFont*) pDC->SelectObject(&m_Font);
+ pDC->SetBkMode(TRANSPARENT);
+ pDC->DrawText(m_strCustomText, TextButtonRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+ pDC->SelectObject(pOldFont);
+
+ return;
+ }
+
+ // For the Default Text area
+ if (m_strDefaultText.GetLength() && nIndex == DEFAULT_BOX_VALUE)
+ {
+ // Fill background
+ pDC->FillSolidRect(m_DefaultTextRect, ::GetSysColor(COLOR_3DFACE));
+
+ // The extent of the actual text button
+ CRect TextButtonRect = m_DefaultTextRect;
+ TextButtonRect.DeflateRect(1,1);
+
+ // fill background
+ if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
+ pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DLIGHT));
+ else
+ pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));
+
+ // Draw thin line around text
+ CRect LineRect = TextButtonRect;
+ LineRect.DeflateRect(2*m_nMargin,2*m_nMargin);
+ CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
+ CPen* pOldPen = pDC->SelectObject(&pen);
+ pDC->SelectStockObject(NULL_BRUSH);
+ pDC->Rectangle(LineRect);
+ pDC->SelectObject(pOldPen);
+
+ // Draw button
+ if (m_nCurrentSel == nIndex)
+ pDC->DrawEdge(TextButtonRect, BDR_RAISEDINNER, BF_RECT);
+ else if (m_nChosenColourSel == nIndex)
+ pDC->DrawEdge(TextButtonRect, BDR_SUNKENOUTER, BF_RECT);
+
+ // Draw custom text
+ CFont *pOldFont = (CFont*) pDC->SelectObject(&m_Font);
+ pDC->SetBkMode(TRANSPARENT);
+ pDC->DrawText(m_strDefaultText, TextButtonRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+ pDC->SelectObject(pOldFont);
+
+ return;
+ }
+
+ CRect rect;
+ if (!GetCellRect(nIndex, rect))
+ return;
+
+ // Select and realize the palette
+ CPalette* pOldPalette = NULL;
+ if (pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
+ {
+ pOldPalette = pDC->SelectPalette(&m_Palette, FALSE);
+ pDC->RealizePalette();
+ }
+
+ // fill background
+ if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
+ pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DHILIGHT));
+ else
+ pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));
+
+ // Draw button
+ if (m_nCurrentSel == nIndex)
+ pDC->DrawEdge(rect, BDR_RAISEDINNER, BF_RECT);
+ else if (m_nChosenColourSel == nIndex)
+ pDC->DrawEdge(rect, BDR_SUNKENOUTER, BF_RECT);
+
+ CBrush brush(PALETTERGB(GetRValue(GetColour(nIndex)),
+ GetGValue(GetColour(nIndex)),
+ GetBValue(GetColour(nIndex)) ));
+ CPen pen;
+ pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
+
+ CBrush* pOldBrush = (CBrush*) pDC->SelectObject(&brush);
+ CPen* pOldPen = (CPen*) pDC->SelectObject(&pen);
+
+ // Draw the cell colour
+ rect.DeflateRect(m_nMargin+1, m_nMargin+1);
+ pDC->Rectangle(rect);
+
+ // restore DC and cleanup
+ pDC->SelectObject(pOldBrush);
+ pDC->SelectObject(pOldPen);
+ brush.DeleteObject();
+ pen.DeleteObject();
+
+ if (pOldPalette && pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
+ pDC->SelectPalette(pOldPalette, FALSE);
+}
+
+BOOL CColourPopup::OnQueryNewPalette()
+{
+ Invalidate();
+ return CWnd::OnQueryNewPalette();
+}
+
+void CColourPopup::OnPaletteChanged(CWnd* pFocusWnd)
+{
+ CWnd::OnPaletteChanged(pFocusWnd);
+
+ if (pFocusWnd->GetSafeHwnd() != GetSafeHwnd())
+ Invalidate();
+}
+
+void CColourPopup::OnKillFocus(CWnd* pNewWnd)
+{
+ CWnd::OnKillFocus(pNewWnd);
+
+ ReleaseCapture();
+ //DestroyWindow(); - causes crash when Custom colour dialog appears.
+}
+
+// KillFocus problem fix suggested by Paul Wilkerson.
+void CColourPopup::OnActivateApp(BOOL bActive, DWORD hTask)
+{
+ CWnd::OnActivateApp(bActive, hTask);
+
+ // If Deactivating App, cancel this selection
+ if (!bActive)
+ EndSelection(UM_CPN_SELENDCANCEL);
+}
diff --git a/ColourPopup.h b/ColourPopup.h
new file mode 100644
index 00000000..f56f0b48
--- /dev/null
+++ b/ColourPopup.h
@@ -0,0 +1,120 @@
+#pragma once
+
+// ColourPopup.h : header file
+//
+// Written by Chris Maunder (chrismaunder@codeguru.com)
+// Extended by Alexander Bischofberger (bischofb@informatik.tu-muenchen.de)
+// Copyright (c) 1998.
+//
+// This code may be used in compiled form in any way you desire. This
+// file may be redistributed unmodified by any means PROVIDING it is
+// not sold for profit without the authors written consent, and
+// providing that this notice and the authors name is included. If
+// the source code in this file is used in any commercial application
+// then a simple email would be nice.
+//
+// This file is provided "as is" with no expressed or implied warranty.
+// The author accepts no liability if it causes any damage whatsoever.
+// It's free - so you get what you pay for.
+
+
+// forward declaration
+class CColourPicker;
+
+// To hold the colours and their names
+typedef struct
+{
+ COLORREF crColour;
+ TCHAR *szName;
+}
+ColourTableEntry;
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CColourPopup window
+
+class CColourPopup : public CWnd
+{
+// Construction
+public:
+ CColourPopup();
+ CColourPopup(CPoint p, COLORREF crColour, CWnd* pParentWnd,
+ LPCTSTR szDefaultText = NULL, LPCTSTR szCustomText = NULL,
+ COLORREF* colourArray = NULL,int NumberOfColours = 0);
+ void Initialise();
+
+// Attributes
+public:
+
+// Operations
+public:
+ BOOL Create(CPoint p, COLORREF crColour, CWnd* pParentWnd, LPCTSTR szDefaultText = NULL,
+ LPCTSTR szCustomText = NULL);
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CColourPopup)
+ public:
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+ //}}AFX_VIRTUAL
+
+// Implementation
+public:
+ virtual ~CColourPopup();
+
+protected:
+ COLORREF* colourArrayPassed;
+ BOOL GetCellRect(int nIndex, const LPRECT& rect);
+ void FindCellFromColour(COLORREF crColour);
+ void SetWindowSize();
+ void CreateToolTips();
+ void ChangeSelection(int nIndex);
+ void EndSelection(int nMessage);
+ void DrawCell(CDC* pDC, int nIndex);
+
+ COLORREF GetColour(int nIndex)
+ {
+ if(colourArrayPassed==NULL)
+ return m_crColours[nIndex].crColour;
+ else
+ return colourArrayPassed[nIndex];
+ }
+ LPCTSTR GetColourName(int nIndex) { return m_crColours[nIndex].szName; }
+ int GetIndex(int row, int col) const;
+ int GetRow(int nIndex) const;
+ int GetColumn(int nIndex) const;
+
+// protected attributes
+protected:
+ static ColourTableEntry m_crColours[];
+ int m_nNumColours;
+ int m_nNumColumns, m_nNumRows;
+ int m_nBoxSize, m_nMargin;
+ int m_nCurrentSel;
+ int m_nChosenColourSel;
+ CString m_strDefaultText;
+ CString m_strCustomText;
+ CRect m_CustomTextRect, m_DefaultTextRect, m_WindowRect;
+ CFont m_Font;
+ CPalette m_Palette;
+ COLORREF m_crInitialColour, m_crColour;
+ CToolTipCtrl m_ToolTip;
+ CWnd* m_pParent;
+ BOOL m_bChildWindowVisible;
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CColourPopup)
+ afx_msg void OnNcDestroy();
+ afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
+ afx_msg void OnPaint();
+ afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+ afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
+ afx_msg BOOL OnQueryNewPalette();
+ afx_msg void OnPaletteChanged(CWnd* pFocusWnd);
+ afx_msg void OnKillFocus(CWnd* pNewWnd);
+ afx_msg void OnActivateApp(BOOL bActive, DWORD hTask);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
diff --git a/ComboBoxEx2.cpp b/ComboBoxEx2.cpp
new file mode 100644
index 00000000..105a3440
--- /dev/null
+++ b/ComboBoxEx2.cpp
@@ -0,0 +1,193 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "ComboBoxEx2.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CComboBoxEx2
+
+IMPLEMENT_DYNAMIC(CComboBoxEx2, CComboBoxEx)
+
+BEGIN_MESSAGE_MAP(CComboBoxEx2, CComboBoxEx)
+END_MESSAGE_MAP()
+
+CComboBoxEx2::CComboBoxEx2()
+{
+}
+
+CComboBoxEx2::~CComboBoxEx2()
+{
+}
+
+int CComboBoxEx2::AddItem(LPCTSTR pszText, int iImage)
+{
+ COMBOBOXEXITEM cbi = {0};
+ cbi.mask = CBEIF_TEXT;
+ cbi.iItem = -1;
+ cbi.pszText = (LPTSTR)pszText;
+ if (iImage != -1)
+ {
+ cbi.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
+ cbi.iImage = iImage;
+ cbi.iSelectedImage = iImage;
+ }
+ return InsertItem(&cbi);
+}
+
+BOOL CComboBoxEx2::PreTranslateMessage(MSG* pMsg)
+{
+ // there seems to be no way that we get the WM_CHARTOITEM for this control
+ ASSERT( pMsg->message != WM_CHARTOITEM );
+
+ if (pMsg->message == WM_KEYDOWN)
+ {
+ UINT uChar = MapVirtualKey(pMsg->wParam, 2);
+ if (uChar != 0)
+ {
+ // CComboBox::SelectString seems also not to work
+ CComboBox* pctrlCB = GetComboBoxCtrl();
+ if (pctrlCB != NULL)
+ {
+ int iCount = pctrlCB->GetCount();
+ for (int i = 0; i < iCount; i++)
+ {
+ CString strItem;
+ pctrlCB->GetLBText(i, strItem);
+ if (strItem.IsEmpty())
+ continue;
+
+ //those casts are indeed all(!) needed to get that thing (at least!) running correctly for ANSI code pages,
+ //if that will also work for MBCS code pages has to be tested..
+ UINT uFirstChar = (UINT)(_TUCHAR)strItem[0];
+ UINT uFirstCharLower = (UINT)(_TUCHAR)_totlower((_TINT)(uFirstChar));
+ UINT uTheChar = (UINT)(_TUCHAR)_totlower((_TINT)((UINT)uChar));
+ if (uFirstCharLower == uTheChar){
+ SetCurSel(i);
+ GetParent()->SendMessage(WM_COMMAND, MAKELONG((WORD)GetWindowLong(m_hWnd, GWL_ID), CBN_SELCHANGE), (LPARAM)m_hWnd);
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ return CComboBoxEx::PreTranslateMessage(pMsg);
+}
+
+// Win98: This function does not work under Win98 ?
+BOOL CComboBoxEx2::SelectString(LPCTSTR pszText)
+{
+ // CComboBox::SelectString seems also not to work
+ CComboBox* pctrlCB = GetComboBoxCtrl();
+ if (pctrlCB != NULL)
+ {
+ int iCount = pctrlCB->GetCount();
+ for (int i = 0; i < iCount; i++)
+ {
+ CString strItem;
+ pctrlCB->GetLBText(i, strItem);
+ if (strItem == pszText)
+ {
+ SetCurSel(i);
+ GetParent()->SendMessage(WM_COMMAND, MAKELONG((WORD)GetWindowLong(m_hWnd, GWL_ID), CBN_SELCHANGE), (LPARAM)m_hWnd);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+BOOL CComboBoxEx2::SelectItemDataStringA(LPCSTR pszText)
+{
+ CComboBox* pctrlCB = GetComboBoxCtrl();
+ if (pctrlCB != NULL)
+ {
+ int iCount = pctrlCB->GetCount();
+ for (int i = 0; i < iCount; i++)
+ {
+ void* pvItemData = GetItemDataPtr(i);
+ if (pvItemData && strcmp((LPCSTR)pvItemData, pszText) == 0)
+ {
+ SetCurSel(i);
+ GetParent()->SendMessage(WM_COMMAND, MAKELONG((WORD)GetWindowLong(m_hWnd, GWL_ID), CBN_SELCHANGE), (LPARAM)m_hWnd);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+void UpdateHorzExtent(CComboBox &rctlComboBox, int iIconWidth)
+{
+ int iItemCount = rctlComboBox.GetCount();
+ if (iItemCount > 0)
+ {
+ CDC *pDC = rctlComboBox.GetDC();
+ if (pDC != NULL)
+ {
+ // *** To get *ACCURATE* results from 'GetOutputTextExtent' one *MUST*
+ // *** explicitly set the font!
+ CFont *pOldFont = pDC->SelectObject(rctlComboBox.GetFont());
+
+ CString strItem;
+ int iMaxWidth = 0;
+ for (int i = 0; i < iItemCount; i++)
+ {
+ rctlComboBox.GetLBText(i, strItem);
+ int iItemWidth = pDC->GetOutputTextExtent(strItem, strItem.GetLength()).cx;
+ if (iItemWidth > iMaxWidth)
+ iMaxWidth = iItemWidth;
+ }
+
+ pDC->SelectObject(pOldFont);
+ rctlComboBox.ReleaseDC(pDC);
+
+ // Depending on the string (lot of "M" or lot of "i") sometime the
+ // width is just a few pixels too small!
+ iMaxWidth += 4;
+ if (iIconWidth)
+ iMaxWidth += 2 + iIconWidth + 2;
+ rctlComboBox.SetHorizontalExtent(iMaxWidth);
+ if (rctlComboBox.GetDroppedWidth() < iMaxWidth)
+ rctlComboBox.SetDroppedWidth(iMaxWidth);
+ }
+ }
+ else
+ rctlComboBox.SetHorizontalExtent(0);
+}
+
+HWND GetComboBoxEditCtrl(CComboBox& cb)
+{
+ CWnd* pWnd = cb.GetWindow(GW_CHILD);
+ while (pWnd)
+ {
+ CHAR szClassName[MAX_PATH];
+ if (::GetClassNameA(*pWnd, szClassName, ARRSIZE(szClassName)))
+ {
+ if (__ascii_stricmp(szClassName, "EDIT") == 0)
+ return pWnd->m_hWnd;
+ }
+ pWnd = pWnd->GetNextWindow();
+ }
+ return NULL;
+}
diff --git a/ComboBoxEx2.h b/ComboBoxEx2.h
new file mode 100644
index 00000000..f460a288
--- /dev/null
+++ b/ComboBoxEx2.h
@@ -0,0 +1,20 @@
+#pragma once
+
+class CComboBoxEx2 : public CComboBoxEx
+{
+ DECLARE_DYNAMIC(CComboBoxEx2)
+public:
+ CComboBoxEx2();
+ virtual ~CComboBoxEx2();
+
+ int AddItem(LPCTSTR pszText, int iImage);
+ BOOL SelectString(LPCTSTR pszText);
+ BOOL SelectItemDataStringA(LPCSTR pszText);
+
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+
+protected:
+ DECLARE_MESSAGE_MAP()
+};
+
+void UpdateHorzExtent(CComboBox &rctlComboBox, int iIconWidth);
diff --git a/CommentDialog.cpp b/CommentDialog.cpp
new file mode 100644
index 00000000..6cb167c4
--- /dev/null
+++ b/CommentDialog.cpp
@@ -0,0 +1,348 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "CommentDialog.h"
+#include "KnownFile.h"
+#include "PartFile.h"
+#include "OtherFunctions.h"
+#include "Opcodes.h"
+#include "StringConversion.h"
+#include "UpDownClient.h"
+#include "kademlia/kademlia/kademlia.h"
+#include "kademlia/kademlia/SearchManager.h"
+#include "kademlia/kademlia/Search.h"
+#include "UserMsgs.h"
+#include "searchlist.h"
+#include "sharedfilelist.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+// CommentDialog dialog
+
+IMPLEMENT_DYNAMIC(CCommentDialog, CResizablePage)
+
+BEGIN_MESSAGE_MAP(CCommentDialog, CResizablePage)
+ ON_BN_CLICKED(IDC_RESET, OnBnClickedReset)
+ ON_MESSAGE(UM_DATA_CHANGED, OnDataChanged)
+ ON_EN_CHANGE(IDC_CMT_TEXT, OnEnChangeCmtText)
+ ON_CBN_SELENDOK(IDC_RATELIST, OnCbnSelendokRatelist)
+ ON_CBN_SELCHANGE(IDC_RATELIST, OnCbnSelchangeRatelist)
+ ON_BN_CLICKED(IDC_SEARCHKAD, OnBnClickedSearchKad)
+ ON_WM_TIMER()
+ ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+CCommentDialog::CCommentDialog()
+ : CResizablePage(CCommentDialog::IDD, 0)
+{
+ m_paFiles = NULL;
+ m_bDataChanged = false;
+ m_strCaption = GetResString(IDS_COMMENT);
+ m_psp.pszTitle = m_strCaption;
+ m_psp.dwFlags |= PSP_USETITLE;
+ m_bMergedComment = false;
+ m_bSelf = false;
+ m_timer = 0;
+ m_bEnabled = true;
+}
+
+CCommentDialog::~CCommentDialog()
+{
+}
+
+void CCommentDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CResizablePage::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_RATELIST, m_ratebox);
+ DDX_Control(pDX, IDC_LST, m_lstComments);
+}
+
+void CCommentDialog::OnTimer(UINT /*nIDEvent*/)
+{
+ RefreshData(false);
+}
+
+BOOL CCommentDialog::OnInitDialog()
+{
+ CResizablePage::OnInitDialog();
+ InitWindowStyles(this);
+
+ AddAnchor(IDC_LST, TOP_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_CMT_LQUEST, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_CMT_LAIDE, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_CMT_TEXT, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_RATEQUEST, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_RATEHELP, TOP_LEFT, TOP_RIGHT);
+ AddAnchor(IDC_USERCOMMENTS, TOP_LEFT, BOTTOM_RIGHT);
+ AddAnchor(IDC_RESET, TOP_RIGHT);
+ AddAnchor(IDC_SEARCHKAD, BOTTOM_RIGHT);
+
+ m_lstComments.Init();
+ Localize();
+
+ // start time for calling 'RefreshData'
+ VERIFY( (m_timer = SetTimer(301, 5000, 0)) != NULL );
+
+ return TRUE;
+}
+
+BOOL CCommentDialog::OnSetActive()
+{
+ if (!CResizablePage::OnSetActive())
+ return FALSE;
+ if (m_bDataChanged)
+ {
+ bool bContainsSharedKnownFile = false;;
+ int iRating = -1;
+ m_bMergedComment = false;
+ CString strComment;
+ for (int i = 0; i < m_paFiles->GetSize(); i++)
+ {
+ if (!(*m_paFiles)[i]->IsKindOf(RUNTIME_CLASS(CKnownFile)))
+ continue;
+ CKnownFile* file = STATIC_DOWNCAST(CKnownFile, (*m_paFiles)[i]);
+ // we actually could show, add and even search for comments on kad for known but not shared files,
+ // but we don't publish coments entered by the user if the file is not shared (which might be changed at some point)
+ // so make sure we don't let him think he can comment and disable the dialog for such files
+ if (theApp.sharedfiles->GetFileByID(file->GetFileHash()) == NULL)
+ continue;
+ bContainsSharedKnownFile = true;
+ if (i == 0)
+ {
+ strComment = file->GetFileComment();
+ iRating = file->GetFileRating();
+ }
+ else
+ {
+ if (!m_bMergedComment && strComment.Compare(file->GetFileComment()) != 0)
+ {
+ strComment.Empty();
+ m_bMergedComment = true;
+ }
+ if (iRating != -1 && (UINT)iRating != file->GetFileRating())
+ iRating = -1;
+ }
+ }
+ m_bSelf = true;
+ SetDlgItemText(IDC_CMT_TEXT, strComment);
+ ((CEdit*)GetDlgItem(IDC_CMT_TEXT))->SetLimitText(MAXFILECOMMENTLEN);
+ m_ratebox.SetCurSel(iRating);
+ m_bSelf = false;
+ EnableDialog(bContainsSharedKnownFile);
+
+ m_bDataChanged = false;
+
+ RefreshData();
+ }
+
+ return TRUE;
+}
+
+LRESULT CCommentDialog::OnDataChanged(WPARAM, LPARAM)
+{
+ m_bDataChanged = true;
+ return 1;
+}
+
+void CCommentDialog::OnBnClickedReset()
+{
+ SetDlgItemText(IDC_CMT_TEXT, _T(""));
+ m_bMergedComment = false;
+ m_ratebox.SetCurSel(0);
+}
+
+BOOL CCommentDialog::OnApply()
+{
+ if (m_bEnabled && !m_bDataChanged)
+ {
+ CString strComment;
+ GetDlgItem(IDC_CMT_TEXT)->GetWindowText(strComment);
+ int iRating = m_ratebox.GetCurSel();
+ for (int i = 0; i < m_paFiles->GetSize(); i++)
+ {
+ if (!(*m_paFiles)[i]->IsKindOf(RUNTIME_CLASS(CKnownFile)))
+ continue;
+ CKnownFile* file = STATIC_DOWNCAST(CKnownFile, (*m_paFiles)[i]);
+ if (theApp.sharedfiles->GetFileByID(file->GetFileHash()) == NULL)
+ continue;
+ if (!strComment.IsEmpty() || !m_bMergedComment)
+ file->SetFileComment(strComment);
+ if (iRating != -1)
+ file->SetFileRating(iRating);
+ }
+ }
+ return CResizablePage::OnApply();
+}
+
+void CCommentDialog::Localize(void)
+{
+ GetDlgItem(IDC_RESET)->SetWindowText(GetResString(IDS_PW_RESET));
+
+ GetDlgItem(IDC_CMT_LQUEST)->SetWindowText(GetResString(IDS_CMT_QUEST));
+ GetDlgItem(IDC_CMT_LAIDE)->SetWindowText(GetResString(IDS_CMT_AIDE));
+
+ GetDlgItem(IDC_RATEQUEST)->SetWindowText(GetResString(IDS_CMT_RATEQUEST));
+ GetDlgItem(IDC_RATEHELP)->SetWindowText(GetResString(IDS_CMT_RATEHELP));
+
+ GetDlgItem(IDC_USERCOMMENTS)->SetWindowText(GetResString(IDS_COMMENT));
+ GetDlgItem(IDC_SEARCHKAD)->SetWindowText(GetResString(IDS_SEARCHKAD));
+
+ CImageList iml;
+ iml.Create(16, 16, theApp.m_iDfltImageListColorFlags | ILC_MASK, 0, 1);
+ iml.Add(CTempIconLoader(_T("Rating_NotRated")));
+ iml.Add(CTempIconLoader(_T("Rating_Fake")));
+ iml.Add(CTempIconLoader(_T("Rating_Poor")));
+ iml.Add(CTempIconLoader(_T("Rating_Fair")));
+ iml.Add(CTempIconLoader(_T("Rating_Good")));
+ iml.Add(CTempIconLoader(_T("Rating_Excellent")));
+ m_ratebox.SetImageList(&iml);
+ m_imlRating.DeleteImageList();
+ m_imlRating.Attach(iml.Detach());
+
+ m_ratebox.ResetContent();
+ m_ratebox.AddItem(GetResString(IDS_CMT_NOTRATED), 0);
+ m_ratebox.AddItem(GetResString(IDS_CMT_FAKE), 1);
+ m_ratebox.AddItem(GetResString(IDS_CMT_POOR), 2);
+ m_ratebox.AddItem(GetResString(IDS_CMT_FAIR), 3);
+ m_ratebox.AddItem(GetResString(IDS_CMT_GOOD), 4);
+ m_ratebox.AddItem(GetResString(IDS_CMT_EXCELLENT), 5);
+ UpdateHorzExtent(m_ratebox, 16); // adjust dropped width to ensure all strings are fully visible
+
+ RefreshData();
+}
+
+void CCommentDialog::OnDestroy()
+{
+ m_imlRating.DeleteImageList();
+ CResizablePage::OnDestroy();
+ if (m_timer){
+ KillTimer(m_timer);
+ m_timer = 0;
+ }
+}
+
+void CCommentDialog::OnEnChangeCmtText()
+{
+ if (!m_bSelf)
+ SetModified();
+}
+
+void CCommentDialog::OnCbnSelendokRatelist()
+{
+ if (!m_bSelf)
+ SetModified();
+}
+
+void CCommentDialog::OnCbnSelchangeRatelist()
+{
+ if (!m_bSelf)
+ SetModified();
+}
+
+void CCommentDialog::RefreshData(bool deleteOld)
+{
+ if (deleteOld)
+ m_lstComments.DeleteAllItems();
+
+ if (!m_bEnabled)
+ return;
+
+ bool kadsearchable = true;
+ for (int i = 0; i < m_paFiles->GetSize(); i++)
+ {
+ CAbstractFile* file = STATIC_DOWNCAST(CAbstractFile, (*m_paFiles)[i]);
+ if (file->IsPartFile())
+ {
+ for (POSITION pos = ((CPartFile*)file)->srclist.GetHeadPosition(); pos != NULL; )
+ {
+ CUpDownClient* cur_src = ((CPartFile*)file)->srclist.GetNext(pos);
+ if (cur_src->HasFileRating() || !cur_src->GetFileComment().IsEmpty())
+ m_lstComments.AddItem(cur_src);
+ }
+ }
+ else if (!file->IsKindOf(RUNTIME_CLASS(CKnownFile)))
+ continue;
+ else if (theApp.sharedfiles->GetFileByID(file->GetFileHash()) == NULL)
+ continue;
+
+ const CTypedPtrList& list = file->getNotes();
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL; )
+ {
+ Kademlia::CEntry* entry = list.GetNext(pos);
+ m_lstComments.AddItem(entry);
+ }
+
+ // check if note searches are running for this file(s)
+ if (Kademlia::CSearchManager::AlreadySearchingFor(Kademlia::CUInt128(file->GetFileHash())))
+ kadsearchable = false;
+ }
+
+ CWnd* pWndFocus = GetFocus();
+ if (Kademlia::CKademlia::IsConnected()) {
+ SetDlgItemText(IDC_SEARCHKAD, kadsearchable ? GetResString(IDS_SEARCHKAD) : GetResString(IDS_KADSEARCHACTIVE));
+ GetDlgItem(IDC_SEARCHKAD)->EnableWindow(kadsearchable);
+ }
+ else {
+ SetDlgItemText(IDC_SEARCHKAD, GetResString(IDS_SEARCHKAD));
+ GetDlgItem(IDC_SEARCHKAD)->EnableWindow(FALSE);
+ }
+ if (pWndFocus && pWndFocus->m_hWnd == GetDlgItem(IDC_SEARCHKAD)->m_hWnd)
+ m_lstComments.SetFocus();
+}
+
+void CCommentDialog::OnBnClickedSearchKad()
+{
+ if (m_bEnabled && Kademlia::CKademlia::IsConnected())
+ {
+ bool bSkipped = false;
+ int iMaxSearches = min(m_paFiles->GetSize(), KADEMLIATOTALFILE);
+ for (int i = 0; i < iMaxSearches; i++)
+ {
+ CAbstractFile* file = STATIC_DOWNCAST(CAbstractFile, (*m_paFiles)[i]);
+ if (file && file->IsKindOf(RUNTIME_CLASS(CKnownFile)) && theApp.sharedfiles->GetFileByID(file->GetFileHash()) != NULL)
+ {
+ if (!Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::NOTES, true, Kademlia::CUInt128(file->GetFileHash())))
+ bSkipped = true;
+ else{
+ theApp.searchlist->SetNotesSearchStatus(file->GetFileHash(), true);
+ file->SetKadCommentSearchRunning(true);
+ }
+ }
+ }
+ if (bSkipped)
+ AfxMessageBox(GetResString(IDS_KADSEARCHALREADY), MB_OK | MB_ICONINFORMATION);
+ }
+ RefreshData();
+}
+
+void CCommentDialog::EnableDialog(bool bEnabled)
+{
+ if (m_bEnabled == bEnabled)
+ return;
+ m_bEnabled = bEnabled;
+ GetDlgItem(IDC_LST)->EnableWindow(m_bEnabled ? TRUE : FALSE);
+ GetDlgItem(IDC_CMT_TEXT)->EnableWindow(m_bEnabled ? TRUE : FALSE);
+ GetDlgItem(IDC_RATELIST)->EnableWindow(m_bEnabled ? TRUE : FALSE);
+ GetDlgItem(IDC_RESET)->EnableWindow(m_bEnabled ? TRUE : FALSE);
+ GetDlgItem(IDC_SEARCHKAD)->EnableWindow(m_bEnabled ? TRUE : FALSE);
+}
\ No newline at end of file
diff --git a/CommentDialog.h b/CommentDialog.h
new file mode 100644
index 00000000..e221c819
--- /dev/null
+++ b/CommentDialog.h
@@ -0,0 +1,53 @@
+#pragma once
+#include "ResizableLib/ResizablePage.h"
+#include "ComboBoxEx2.h"
+#include "CommentListCtrl.h"
+
+class CKnownFile;
+namespace Kademlia {
+ class CEntry;
+};
+
+class CCommentDialog : public CResizablePage
+{
+ DECLARE_DYNAMIC(CCommentDialog)
+
+public:
+ CCommentDialog(); // standard constructor
+ virtual ~CCommentDialog();
+
+ void SetFiles(const CSimpleArray* paFiles) { m_paFiles = paFiles; m_bDataChanged = true; }
+
+ // Dialog Data
+ enum { IDD = IDD_COMMENT };
+
+ void Localize();
+
+protected:
+ const CSimpleArray* m_paFiles;
+ bool m_bDataChanged;
+ CComboBoxEx2 m_ratebox;
+ CImageList m_imlRating;
+ CCommentListCtrl m_lstComments;
+ bool m_bMergedComment;
+ bool m_bSelf;
+ uint32 m_timer;
+ bool m_bEnabled;
+
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual BOOL OnInitDialog();
+ virtual BOOL OnSetActive();
+ virtual BOOL OnApply();
+ void RefreshData(bool deleteOld = true);
+ void EnableDialog(bool bEnabled);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnBnClickedSearchKad();
+ afx_msg void OnBnClickedReset();
+ afx_msg LRESULT OnDataChanged(WPARAM, LPARAM);
+ afx_msg void OnEnChangeCmtText();
+ afx_msg void OnCbnSelendokRatelist();
+ afx_msg void OnCbnSelchangeRatelist();
+ afx_msg void OnDestroy();
+ afx_msg void OnTimer(UINT nIDEvent);
+};
diff --git a/CommentDialogLst.cpp b/CommentDialogLst.cpp
new file mode 100644
index 00000000..cc15cb22
--- /dev/null
+++ b/CommentDialogLst.cpp
@@ -0,0 +1,228 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "CommentDialogLst.h"
+#include "PartFile.h"
+#include "UpDownClient.h"
+#include "UserMsgs.h"
+#include "kademlia/kademlia/kademlia.h"
+#include "kademlia/kademlia/SearchManager.h"
+#include "kademlia/kademlia/Search.h"
+#include "searchlist.h"
+#include "InputBox.h"
+#include "DownloadQueue.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+IMPLEMENT_DYNAMIC(CCommentDialogLst, CResizablePage)
+
+BEGIN_MESSAGE_MAP(CCommentDialogLst, CResizablePage)
+ ON_BN_CLICKED(IDOK, OnBnClickedApply)
+ ON_BN_CLICKED(IDC_SEARCHKAD, OnBnClickedSearchKad)
+ ON_BN_CLICKED(IDC_EDITCOMMENTFILTER, OnBnClickedFilter)
+ ON_MESSAGE(UM_DATA_CHANGED, OnDataChanged)
+ ON_WM_TIMER()
+ ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+CCommentDialogLst::CCommentDialogLst()
+ : CResizablePage(CCommentDialogLst::IDD, IDS_CMT_READALL)
+{
+ m_paFiles = NULL;
+ m_bDataChanged = false;
+ m_strCaption = GetResString(IDS_CMT_READALL);
+ m_psp.pszTitle = m_strCaption;
+ m_psp.dwFlags |= PSP_USETITLE;
+ m_paFiles = NULL;
+ m_timer = 0;
+}
+
+CCommentDialogLst::~CCommentDialogLst()
+{
+}
+
+void CCommentDialogLst::DoDataExchange(CDataExchange* pDX)
+{
+ CResizablePage::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_LST, m_lstComments);
+}
+
+void CCommentDialogLst::OnBnClickedApply()
+{
+ CResizablePage::OnOK();
+}
+
+void CCommentDialogLst::OnTimer(UINT /*nIDEvent*/)
+{
+ RefreshData(false);
+}
+
+BOOL CCommentDialogLst::OnInitDialog()
+{
+ CResizablePage::OnInitDialog();
+ InitWindowStyles(this);
+
+ AddAnchor(IDC_LST,TOP_LEFT,BOTTOM_RIGHT);
+ AddAnchor(IDC_SEARCHKAD,BOTTOM_RIGHT);
+ AddAnchor(IDC_EDITCOMMENTFILTER,BOTTOM_LEFT);
+
+ m_lstComments.Init();
+ Localize();
+
+ // start time for calling 'RefreshData'
+ VERIFY( (m_timer = SetTimer(301, 5000, 0)) != NULL );
+
+ return TRUE;
+}
+
+BOOL CCommentDialogLst::OnSetActive()
+{
+ if (!CResizablePage::OnSetActive())
+ return FALSE;
+ if (m_bDataChanged)
+ {
+ RefreshData();
+ m_bDataChanged = false;
+ }
+ return TRUE;
+}
+
+LRESULT CCommentDialogLst::OnDataChanged(WPARAM, LPARAM)
+{
+ m_bDataChanged = true;
+ return 1;
+}
+
+void CCommentDialogLst::OnDestroy()
+{
+ if (m_timer){
+ KillTimer(m_timer);
+ m_timer = 0;
+ }
+}
+
+void CCommentDialogLst::Localize(void)
+{
+ GetDlgItem(IDC_SEARCHKAD)->SetWindowText(GetResString(IDS_SEARCHKAD));
+ GetDlgItem(IDC_EDITCOMMENTFILTER)->SetWindowText(GetResString(IDS_EDITSPAMFILTER));
+}
+
+void CCommentDialogLst::RefreshData(bool deleteOld)
+{
+ if (deleteOld)
+ m_lstComments.DeleteAllItems();
+
+ bool kadsearchable = true;
+ for (int i = 0; i < m_paFiles->GetSize(); i++)
+ {
+ CAbstractFile* file = STATIC_DOWNCAST(CAbstractFile, (*m_paFiles)[i]);
+ if (file->IsPartFile())
+ {
+ for (POSITION pos = ((CPartFile*)file)->srclist.GetHeadPosition(); pos != NULL; )
+ {
+ CUpDownClient* cur_src = ((CPartFile*)file)->srclist.GetNext(pos);
+ if (cur_src->HasFileRating() || !cur_src->GetFileComment().IsEmpty())
+ m_lstComments.AddItem(cur_src);
+ }
+ }
+
+ const CTypedPtrList& list = file->getNotes();
+ for (POSITION pos = list.GetHeadPosition(); pos != NULL; )
+ {
+ Kademlia::CEntry* entry = list.GetNext(pos);
+ m_lstComments.AddItem(entry);
+ }
+ if (file->IsPartFile())
+ ((CPartFile*)file)->UpdateFileRatingCommentAvail();
+
+ // check if note searches are running for this file(s)
+ if (Kademlia::CSearchManager::AlreadySearchingFor(Kademlia::CUInt128(file->GetFileHash())))
+ kadsearchable = false;
+ }
+
+ CWnd* pWndFocus = GetFocus();
+ if (Kademlia::CKademlia::IsConnected()) {
+ SetDlgItemText(IDC_SEARCHKAD, kadsearchable ? GetResString(IDS_SEARCHKAD) : GetResString(IDS_KADSEARCHACTIVE));
+ GetDlgItem(IDC_SEARCHKAD)->EnableWindow(kadsearchable);
+ }
+ else {
+ SetDlgItemText(IDC_SEARCHKAD, GetResString(IDS_SEARCHKAD));
+ GetDlgItem(IDC_SEARCHKAD)->EnableWindow(FALSE);
+ }
+ if (pWndFocus && pWndFocus->m_hWnd == GetDlgItem(IDC_SEARCHKAD)->m_hWnd)
+ m_lstComments.SetFocus();
+}
+
+void CCommentDialogLst::OnBnClickedSearchKad()
+{
+ if (Kademlia::CKademlia::IsConnected())
+ {
+ bool bSkipped = false;
+ int iMaxSearches = min(m_paFiles->GetSize(), KADEMLIATOTALFILE);
+ for (int i = 0; i < iMaxSearches; i++)
+ {
+ CAbstractFile* file = STATIC_DOWNCAST(CAbstractFile, (*m_paFiles)[i]);
+ if (file)
+ {
+ if (!Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::NOTES, true, Kademlia::CUInt128(file->GetFileHash())))
+ bSkipped = true;
+ else{
+ theApp.searchlist->SetNotesSearchStatus(file->GetFileHash(), true);
+ file->SetKadCommentSearchRunning(true);
+ }
+ }
+ }
+ if (bSkipped)
+ AfxMessageBox(GetResString(IDS_KADSEARCHALREADY), MB_OK | MB_ICONINFORMATION);
+ }
+ RefreshData();
+}
+
+void CCommentDialogLst::OnBnClickedFilter()
+{
+ InputBox inputbox;
+ inputbox.SetLabels(GetResString(IDS_EDITSPAMFILTERCOMMENTS), GetResString(IDS_FILTERCOMMENTSLABEL), thePrefs.GetCommentFilter());
+ inputbox.DoModal();
+ if (!inputbox.WasCancelled()){
+ CString strCommentFilters = inputbox.GetInput();
+ strCommentFilters.MakeLower();
+ CString strNewCommentFilters;
+ int curPos = 0;
+ CString strFilter(strCommentFilters.Tokenize(_T("|"), curPos));
+ while (!strFilter.IsEmpty())
+ {
+ strFilter.Trim();
+ if (!strNewCommentFilters.IsEmpty())
+ strNewCommentFilters += _T('|');
+ strNewCommentFilters += strFilter;
+ strFilter = strCommentFilters.Tokenize(_T("|"), curPos);
+ }
+ if (thePrefs.GetCommentFilter() != strNewCommentFilters){
+ thePrefs.SetCommentFilter(strNewCommentFilters);
+ theApp.downloadqueue->RefilterAllComments();
+ RefreshData();
+ }
+ }
+}
+
+
diff --git a/CommentDialogLst.h b/CommentDialogLst.h
new file mode 100644
index 00000000..4e69b328
--- /dev/null
+++ b/CommentDialogLst.h
@@ -0,0 +1,45 @@
+#pragma once
+#include "ResizableLib/ResizablePage.h"
+#include "CommentListCtrl.h"
+
+class CPartFile;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CCommentDialogLst
+
+class CCommentDialogLst : public CResizablePage
+{
+ DECLARE_DYNAMIC(CCommentDialogLst)
+
+public:
+ CCommentDialogLst();
+ virtual ~CCommentDialogLst();
+
+ void SetFiles(const CSimpleArray* paFiles) { m_paFiles = paFiles; m_bDataChanged = true; }
+
+// Dialog Data
+ enum { IDD = IDD_COMMENTLST };
+
+protected:
+ CString m_strCaption;
+ CCommentListCtrl m_lstComments;
+ const CSimpleArray* m_paFiles;
+ bool m_bDataChanged;
+ uint32 m_timer;
+
+ void Localize();
+ void RefreshData(bool deleteOld = true);
+
+ virtual BOOL OnInitDialog();
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual BOOL OnSetActive();
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnBnClickedApply();
+ afx_msg void OnBnClickedSearchKad();
+ afx_msg void OnBnClickedFilter();
+ afx_msg LRESULT OnDataChanged(WPARAM, LPARAM);
+ afx_msg void OnDestroy();
+ afx_msg void OnTimer(UINT nIDEvent);
+};
diff --git a/CommentListCtrl.cpp b/CommentListCtrl.cpp
new file mode 100644
index 00000000..7214099e
--- /dev/null
+++ b/CommentListCtrl.cpp
@@ -0,0 +1,246 @@
+//this file is part of eMule
+//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "CommentListCtrl.h"
+#include "OtherFunctions.h"
+#include "MenuCmds.h"
+#include "TitleMenu.h"
+#include "emule.h"
+#include "UpDownClient.h"
+#include "kademlia/kademlia/Entry.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#endif
+
+enum ECols
+{
+ colRating = 0,
+ colComment,
+ colFileName,
+ colUserName,
+ colOrigin
+};
+
+IMPLEMENT_DYNAMIC(CCommentListCtrl, CMuleListCtrl)
+
+BEGIN_MESSAGE_MAP(CCommentListCtrl, CMuleListCtrl)
+ ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnClick)
+ ON_NOTIFY_REFLECT(LVN_DELETEITEM, OnLvnDeleteItem)
+ ON_WM_CONTEXTMENU()
+END_MESSAGE_MAP()
+
+CCommentListCtrl::CCommentListCtrl()
+{
+}
+
+CCommentListCtrl::~CCommentListCtrl()
+{
+}
+
+void CCommentListCtrl::Init(void)
+{
+ SetPrefsKey(_T("CommentListCtrl"));
+ ASSERT( (GetStyle() & LVS_SINGLESEL) == 0 );
+ SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+
+ InsertColumn(colRating, GetResString(IDS_QL_RATING), LVCFMT_LEFT, 80);
+ InsertColumn(colComment, GetResString(IDS_COMMENT), LVCFMT_LEFT, 340);
+ InsertColumn(colFileName, GetResString(IDS_DL_FILENAME), LVCFMT_LEFT, DFLT_FILENAME_COL_WIDTH);
+ InsertColumn(colUserName, GetResString(IDS_QL_USERNAME), LVCFMT_LEFT, DFLT_CLIENTNAME_COL_WIDTH);
+ InsertColumn(colOrigin, GetResString(IDS_NETWORK), LVCFMT_LEFT, 80);
+
+ CImageList iml;
+ iml.Create(16, 16, theApp.m_iDfltImageListColorFlags | ILC_MASK, 0, 1);
+ iml.Add(CTempIconLoader(_T("Rating_NotRated")));
+ iml.Add(CTempIconLoader(_T("Rating_Fake")));
+ iml.Add(CTempIconLoader(_T("Rating_Poor")));
+ iml.Add(CTempIconLoader(_T("Rating_Fair")));
+ iml.Add(CTempIconLoader(_T("Rating_Good")));
+ iml.Add(CTempIconLoader(_T("Rating_Excellent")));
+ CImageList* pimlOld = SetImageList(&iml, LVSIL_SMALL);
+ iml.Detach();
+ if (pimlOld)
+ pimlOld->DeleteImageList();
+
+ LoadSettings();
+ SetSortArrow();
+ SortItems(SortProc, MAKELONG(GetSortItem(), (GetSortAscending() ? 0 : 1)));
+}
+
+int CCommentListCtrl::SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ const SComment *item1 = (SComment *)lParam1;
+ const SComment *item2 = (SComment *)lParam2;
+ if (item1 == NULL || item2 == NULL)
+ return 0;
+
+ int iResult;
+ switch (LOWORD(lParamSort))
+ {
+ case colRating:
+ if (item1->m_iRating < item2->m_iRating)
+ iResult = -1;
+ else if (item1->m_iRating > item2->m_iRating)
+ iResult = 1;
+ else
+ iResult = 0;
+ break;
+
+ case colComment:
+ iResult = CompareLocaleStringNoCase(item1->m_strComment, item2->m_strComment);
+ break;
+
+ case colFileName:
+ iResult = CompareLocaleStringNoCase(item1->m_strFileName, item2->m_strFileName);
+ break;
+
+ case colUserName:
+ iResult = CompareLocaleStringNoCase(item1->m_strUserName, item2->m_strUserName);
+ break;
+
+ case colOrigin:
+ if (item1->m_iOrigin < item2->m_iOrigin)
+ iResult = -1;
+ else if (item1->m_iOrigin > item2->m_iOrigin)
+ iResult = 1;
+ else
+ iResult = 0;
+ break;
+
+ default:
+ ASSERT(0);
+ return 0;
+ }
+ if (HIWORD(lParamSort))
+ iResult = -iResult;
+ return iResult;
+}
+
+void CCommentListCtrl::OnLvnColumnClick(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR);
+
+ // Determine ascending based on whether already sorted on this column
+ int iSortItem = GetSortItem();
+ bool bOldSortAscending = GetSortAscending();
+ bool bSortAscending = (iSortItem != pNMLV->iSubItem) ? true : !bOldSortAscending;
+
+ // Item is column clicked
+ iSortItem = pNMLV->iSubItem;
+
+ // Sort table
+ UpdateSortHistory(MAKELONG(iSortItem, (bSortAscending ? 0 : 0x0001)));
+ SetSortArrow(iSortItem, bSortAscending);
+ SortItems(SortProc, MAKELONG(iSortItem, (bSortAscending ? 0 : 0x0001)));
+
+ *pResult = 0;
+}
+
+void CCommentListCtrl::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
+{
+ UINT flag = MF_STRING;
+ if (GetNextItem(-1, LVIS_SELECTED | LVIS_FOCUSED) == -1)
+ flag = MF_GRAYED;
+
+ CTitleMenu popupMenu;
+ popupMenu.CreatePopupMenu();
+ popupMenu.AppendMenu(MF_STRING | flag, MP_COPYSELECTED, GetResString(IDS_COPY));
+
+ GetPopupMenuPos(*this, point);
+ popupMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
+ VERIFY( popupMenu.DestroyMenu() );
+}
+
+BOOL CCommentListCtrl::OnCommand(WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam)
+ {
+ case MP_COPYSELECTED: {
+ CString strText;
+ POSITION posItem = GetFirstSelectedItemPosition();
+ while (posItem) {
+ int iItem = GetNextSelectedItem(posItem);
+ if (iItem >= 0) {
+ CString strComment = GetItemText(iItem, colComment);
+ if (!strComment.IsEmpty()) {
+ if (!strText.IsEmpty())
+ strText += _T("\r\n");
+ strText += strComment;
+ }
+ }
+ }
+ theApp.CopyTextToClipboard(strText);
+ break;
+ }
+ }
+ return CMuleListCtrl::OnCommand(wParam, lParam);
+}
+
+int CCommentListCtrl::FindClientComment(const void* pClientCookie)
+{
+ int iItems = GetItemCount();
+ for (int i = 0; i < iItems; i++)
+ {
+ const SComment* pComment = (SComment*)GetItemData(i);
+ if (pComment && pComment->m_pClientCookie == pClientCookie)
+ return i;
+ }
+ return -1;
+}
+
+void CCommentListCtrl::AddComment(const SComment* pComment)
+{
+ int iItem = InsertItem(LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM,
+ 0, GetRateString(pComment->m_iRating),
+ 0, 0, pComment->m_iRating, (LPARAM)pComment);
+ SetItemText(iItem, colComment, pComment->m_strComment);
+ SetItemText(iItem, colFileName, pComment->m_strFileName);
+ SetItemText(iItem, colUserName, pComment->m_strUserName);
+ SetItemText(iItem, colOrigin, pComment->m_iOrigin == 0 ? _T("eD2K") : _T("Kad"));
+}
+
+void CCommentListCtrl::AddItem(const CUpDownClient* client)
+{
+ const void* pClientCookie = client;
+ if (FindClientComment(pClientCookie) != -1)
+ return;
+ int iRating = client->GetFileRating();
+ SComment* pComment = new SComment(pClientCookie, iRating, client->GetFileComment(),
+ client->GetClientFilename(), client->GetUserName(), 0/*eD2K*/);
+ AddComment(pComment);
+}
+
+void CCommentListCtrl::AddItem(const Kademlia::CEntry* entry)
+{
+ const void* pClientCookie = entry;
+ if (FindClientComment(pClientCookie) != -1)
+ return;
+ int iRating = (int)entry->GetIntTagValue(TAG_FILERATING);
+ SComment* pComment = new SComment(pClientCookie, iRating, entry->GetStrTagValue(TAG_DESCRIPTION),
+ entry->GetCommonFileName(), _T(""), 1/*Kad*/);
+ AddComment(pComment);
+}
+
+void CCommentListCtrl::OnLvnDeleteItem(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR);
+ delete (SComment*)pNMLV->lParam;
+ *pResult = 0;
+}
diff --git a/CommentListCtrl.h b/CommentListCtrl.h
new file mode 100644
index 00000000..226ff4b4
--- /dev/null
+++ b/CommentListCtrl.h
@@ -0,0 +1,65 @@
+//this file is part of eMule
+//Copyright (C)2002-2005 Merkur ( devs@emule-project.net / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#pragma once
+#include "MuleListCtrl.h"
+
+class CUpDownClient;
+namespace Kademlia
+{
+ class CEntry;
+};
+
+class CCommentListCtrl : public CMuleListCtrl
+{
+ DECLARE_DYNAMIC(CCommentListCtrl)
+
+public:
+ CCommentListCtrl();
+ virtual ~CCommentListCtrl();
+
+ void Init();
+ void AddItem(const CUpDownClient* client);
+ void AddItem(const Kademlia::CEntry* entry);
+
+protected:
+ struct SComment
+ {
+ SComment(const void* pClientCookie, int iRating, const CString& strComment,
+ const CString& strFileName, const CString& strUserName, int iOrigin)
+ : m_pClientCookie(pClientCookie), m_iRating(iRating),
+ m_strComment(strComment), m_strFileName(strFileName),
+ m_strUserName(strUserName), m_iOrigin(iOrigin)
+ { }
+
+ const void* m_pClientCookie;
+ int m_iRating;
+ CString m_strComment;
+ CString m_strFileName;
+ CString m_strUserName;
+ int m_iOrigin; // 0=eD2K, 1=Kad
+ };
+ void AddComment(const SComment* pComment);
+ int FindClientComment(const void* pCookie);
+
+ static int CALLBACK SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+
+ DECLARE_MESSAGE_MAP()
+ afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
+ afx_msg void OnLvnColumnClick(NMHDR *pNMHDR, LRESULT *pResult);
+ afx_msg void OnLvnDeleteItem(NMHDR *pNMHDR, LRESULT *pResult);
+};
diff --git a/CorruptionBlackBox.cpp b/CorruptionBlackBox.cpp
new file mode 100644
index 00000000..c8b22013
--- /dev/null
+++ b/CorruptionBlackBox.cpp
@@ -0,0 +1,395 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "StdAfx.h"
+#include "corruptionblackbox.h"
+#include "knownfile.h"
+#include "updownclient.h"
+#include "log.h"
+#include "emule.h"
+#include "clientlist.h"
+#include "opcodes.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+#define CBB_BANTHRESHOLD 32 //% max corrupted data
+
+CCBBRecord::CCBBRecord(uint64 nStartPos, uint64 nEndPos, uint32 dwIP, EBBRStatus BBRStatus){
+ if (nStartPos > nEndPos){
+ ASSERT( false );
+ return;
+ }
+ m_nStartPos = nStartPos;
+ m_nEndPos = nEndPos;
+ m_dwIP = dwIP;
+ m_BBRStatus = BBRStatus;
+}
+
+CCBBRecord& CCBBRecord::operator=(const CCBBRecord& cv)
+{
+ m_nStartPos = cv.m_nStartPos;
+ m_nEndPos = cv.m_nEndPos;
+ m_dwIP = cv.m_dwIP;
+ m_BBRStatus = cv.m_BBRStatus;
+ return *this;
+}
+
+bool CCBBRecord::Merge(uint64 nStartPos, uint64 nEndPos, uint32 dwIP, EBBRStatus BBRStatus){
+
+ if (m_dwIP == dwIP && m_BBRStatus == BBRStatus && (nStartPos == m_nEndPos + 1 || nEndPos + 1 == m_nStartPos)){
+ if (nStartPos == m_nEndPos + 1)
+ m_nEndPos = nEndPos;
+ else if (nEndPos + 1 == m_nStartPos)
+ m_nStartPos = nStartPos;
+ else
+ ASSERT( false );
+
+ return true;
+ }
+ else
+ return false;
+}
+
+bool CCBBRecord::CanMerge(uint64 nStartPos, uint64 nEndPos, uint32 dwIP, EBBRStatus BBRStatus){
+
+ if (m_dwIP == dwIP && m_BBRStatus == BBRStatus && (nStartPos == m_nEndPos + 1 || nEndPos + 1 == m_nStartPos)){
+ return true;
+ }
+ else
+ return false;
+}
+
+void CCorruptionBlackBox::Init(EMFileSize nFileSize) {
+ m_aaRecords.SetSize((INT_PTR)((uint64)(nFileSize + (uint64)(PARTSIZE - 1)) / (PARTSIZE)));
+}
+
+void CCorruptionBlackBox::Free() {
+ m_aaRecords.RemoveAll();
+ m_aaRecords.FreeExtra();
+}
+
+void CCorruptionBlackBox::TransferredData(uint64 nStartPos, uint64 nEndPos, const CUpDownClient* pSender){
+ if (nEndPos - nStartPos >= PARTSIZE){
+ ASSERT( false );
+ return;
+ }
+ if (nStartPos > nEndPos){
+ ASSERT( false );
+ return;
+ }
+ uint32 dwSenderIP = pSender->GetIP();
+ // we store records seperated for each part, so we don't have to search all entries everytime
+
+ // convert pos to relative block pos
+ UINT nPart = (UINT)(nStartPos / PARTSIZE);
+ uint64 nRelStartPos = nStartPos - (uint64)nPart*PARTSIZE;
+ uint64 nRelEndPos = nEndPos - (uint64)nPart*PARTSIZE;
+ if (nRelEndPos >= PARTSIZE){
+ // data crosses the partborder, split it
+ nRelEndPos = PARTSIZE-1;
+ uint64 nTmpStartPos = (uint64)nPart*PARTSIZE + nRelEndPos + 1;
+ ASSERT( nTmpStartPos % PARTSIZE == 0); // remove later
+ TransferredData(nTmpStartPos, nEndPos, pSender);
+ }
+ if (nPart >= (UINT)m_aaRecords.GetCount()){
+ //ASSERT( false );
+ m_aaRecords.SetSize(nPart+1);
+ }
+ int posMerge = -1;
+ uint64 ndbgRewritten = 0;
+ for (int i= 0; i < m_aaRecords[nPart].GetCount(); i++){
+ if (m_aaRecords[nPart][i].CanMerge(nRelStartPos, nRelEndPos, dwSenderIP, BBR_NONE)){
+ posMerge = i;
+ }
+ // check if there is already an pending entry and overwrite it
+ else if (m_aaRecords[nPart][i].m_BBRStatus == BBR_NONE){
+ if (m_aaRecords[nPart][i].m_nStartPos >= nRelStartPos && m_aaRecords[nPart][i].m_nEndPos <= nRelEndPos){
+ // old one is included in new one -> delete
+ ndbgRewritten += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart].RemoveAt(i);
+ i--;
+ }
+ else if (m_aaRecords[nPart][i].m_nStartPos < nRelStartPos && m_aaRecords[nPart][i].m_nEndPos > nRelEndPos){
+ // old one includes new one
+ // check if the old one and new one have the same ip
+ if (dwSenderIP != m_aaRecords[nPart][i].m_dwIP){
+ // different IP, means we have to split it 2 times
+ uint64 nTmpEndPos1 = m_aaRecords[nPart][i].m_nEndPos;
+ uint64 nTmpStartPos1 = nRelEndPos + 1;
+ uint64 nTmpStartPos2 = m_aaRecords[nPart][i].m_nStartPos;
+ uint64 nTmpEndPos2 = nRelStartPos - 1;
+ m_aaRecords[nPart][i].m_nEndPos = nRelEndPos;
+ m_aaRecords[nPart][i].m_nStartPos = nRelStartPos;
+ uint32 dwOldIP = m_aaRecords[nPart][i].m_dwIP;
+ m_aaRecords[nPart][i].m_dwIP = dwSenderIP;
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos1,nTmpEndPos1, dwOldIP));
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos2,nTmpEndPos2, dwOldIP));
+ // and are done then
+ }
+ DEBUG_ONLY( AddDebugLogLine(DLP_DEFAULT, false, _T("CorruptionBlackBox: Debug: %i bytes were rewritten and records replaced with new stats (1)"), (nRelEndPos - nRelStartPos)+1) );
+ return;
+ }
+ else if (m_aaRecords[nPart][i].m_nStartPos >= nRelStartPos && m_aaRecords[nPart][i].m_nStartPos <= nRelEndPos){
+ // old one laps over new one on the right site
+ ASSERT( nRelEndPos - m_aaRecords[nPart][i].m_nStartPos > 0 );
+ ndbgRewritten += nRelEndPos - m_aaRecords[nPart][i].m_nStartPos;
+ m_aaRecords[nPart][i].m_nStartPos = nRelEndPos + 1;
+ }
+ else if (m_aaRecords[nPart][i].m_nEndPos >= nRelStartPos && m_aaRecords[nPart][i].m_nEndPos <= nRelEndPos){
+ // old one laps over new one on the left site
+ ASSERT( m_aaRecords[nPart][i].m_nEndPos - nRelStartPos > 0 );
+ ndbgRewritten += m_aaRecords[nPart][i].m_nEndPos - nRelStartPos;
+ m_aaRecords[nPart][i].m_nEndPos = nRelStartPos - 1;
+ }
+ }
+ }
+ if (posMerge != (-1) ){
+ VERIFY( m_aaRecords[nPart][posMerge].Merge(nRelStartPos, nRelEndPos, dwSenderIP, BBR_NONE) );
+ }
+ else
+ m_aaRecords[nPart].Add(CCBBRecord(nRelStartPos, nRelEndPos, dwSenderIP, BBR_NONE));
+
+ if (ndbgRewritten > 0){
+ DEBUG_ONLY( AddDebugLogLine(DLP_DEFAULT, false, _T("CorruptionBlackBox: Debug: %i bytes were rewritten and records replaced with new stats (2)"), ndbgRewritten) );
+ }
+}
+
+void CCorruptionBlackBox::VerifiedData(uint64 nStartPos, uint64 nEndPos){
+ if (nEndPos - nStartPos >= PARTSIZE){
+ ASSERT( false );
+ return;
+ }
+ // convert pos to relative block pos
+ UINT nPart = (UINT)(nStartPos / PARTSIZE);
+ uint64 nRelStartPos = nStartPos - (uint64)nPart*PARTSIZE;
+ uint64 nRelEndPos = nEndPos - (uint64)nPart*PARTSIZE;
+ if (nRelEndPos >= PARTSIZE){
+ ASSERT( false );
+ return;
+ }
+ if (nPart >= (UINT)m_aaRecords.GetCount()){
+ //ASSERT( false );
+ m_aaRecords.SetSize(nPart+1);
+ }
+ uint64 nDbgVerifiedBytes = 0;
+ //uint32 nDbgOldEntries = m_aaRecords[nPart].GetCount();
+#ifdef _DEBUG
+ CMap mapDebug;
+#endif
+ for (int i= 0; i < m_aaRecords[nPart].GetCount(); i++){
+ if (m_aaRecords[nPart][i].m_BBRStatus == BBR_NONE || m_aaRecords[nPart][i].m_BBRStatus == BBR_VERIFIED){
+ if (m_aaRecords[nPart][i].m_nStartPos >= nRelStartPos && m_aaRecords[nPart][i].m_nEndPos <= nRelEndPos){
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_VERIFIED;
+ DEBUG_ONLY(mapDebug.SetAt(m_aaRecords[nPart][i].m_dwIP, 1));
+ }
+ else if (m_aaRecords[nPart][i].m_nStartPos < nRelStartPos && m_aaRecords[nPart][i].m_nEndPos > nRelEndPos){
+ // need to split it 2*
+ uint64 nTmpEndPos1 = m_aaRecords[nPart][i].m_nEndPos;
+ uint64 nTmpStartPos1 = nRelEndPos + 1;
+ uint64 nTmpStartPos2 = m_aaRecords[nPart][i].m_nStartPos;
+ uint64 nTmpEndPos2 = nRelStartPos - 1;
+ m_aaRecords[nPart][i].m_nEndPos = nRelEndPos;
+ m_aaRecords[nPart][i].m_nStartPos = nRelStartPos;
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos1, nTmpEndPos1, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos2, nTmpEndPos2, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_VERIFIED;
+ DEBUG_ONLY(mapDebug.SetAt(m_aaRecords[nPart][i].m_dwIP, 1));
+ }
+ else if (m_aaRecords[nPart][i].m_nStartPos >= nRelStartPos && m_aaRecords[nPart][i].m_nStartPos <= nRelEndPos){
+ // need to split it
+ uint64 nTmpEndPos = m_aaRecords[nPart][i].m_nEndPos;
+ uint64 nTmpStartPos = nRelEndPos + 1;
+ m_aaRecords[nPart][i].m_nEndPos = nRelEndPos;
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos, nTmpEndPos, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_VERIFIED;
+ DEBUG_ONLY(mapDebug.SetAt(m_aaRecords[nPart][i].m_dwIP, 1));
+ }
+ else if (m_aaRecords[nPart][i].m_nEndPos >= nRelStartPos && m_aaRecords[nPart][i].m_nEndPos <= nRelEndPos){
+ // need to split it
+ uint64 nTmpStartPos = m_aaRecords[nPart][i].m_nStartPos;
+ uint64 nTmpEndPos = nRelStartPos - 1;
+ m_aaRecords[nPart][i].m_nStartPos = nRelStartPos;
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos, nTmpEndPos, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_VERIFIED;
+ DEBUG_ONLY(mapDebug.SetAt(m_aaRecords[nPart][i].m_dwIP, 1));
+ }
+ }
+ }
+/*#ifdef _DEBUG
+ uint32 nClients = mapDebug.GetCount();
+#else
+ uint32 nClients = 0;
+#endif
+ AddDebugLogLine(DLP_DEFAULT, false, _T("Found and marked %u recorded bytes of %u as verified in the CorruptionBlackBox records, %u(%u) records found, %u different clients"), nDbgVerifiedBytes, (nEndPos-nStartPos)+1, m_aaRecords[nPart].GetCount(), nDbgOldEntries, nClients);*/
+}
+
+
+
+void CCorruptionBlackBox::CorruptedData(uint64 nStartPos, uint64 nEndPos){
+ if (nEndPos - nStartPos >= EMBLOCKSIZE){
+ ASSERT( false );
+ return;
+ }
+ // convert pos to relative block pos
+ UINT nPart = (UINT)(nStartPos / PARTSIZE);
+ uint64 nRelStartPos = nStartPos - (uint64)nPart*PARTSIZE;
+ uint64 nRelEndPos = nEndPos - (uint64)nPart*PARTSIZE;
+ if (nRelEndPos >= PARTSIZE){
+ ASSERT( false );
+ return;
+ }
+ if (nPart >= (UINT)m_aaRecords.GetCount()){
+ //ASSERT( false );
+ m_aaRecords.SetSize(nPart+1);
+ }
+ uint64 nDbgVerifiedBytes = 0;
+ for (int i= 0; i < m_aaRecords[nPart].GetCount(); i++){
+ if (m_aaRecords[nPart][i].m_BBRStatus == BBR_NONE){
+ if (m_aaRecords[nPart][i].m_nStartPos >= nRelStartPos && m_aaRecords[nPart][i].m_nEndPos <= nRelEndPos){
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_CORRUPTED;
+ }
+ else if (m_aaRecords[nPart][i].m_nStartPos < nRelStartPos && m_aaRecords[nPart][i].m_nEndPos > nRelEndPos){
+ // need to split it 2*
+ uint64 nTmpEndPos1 = m_aaRecords[nPart][i].m_nEndPos;
+ uint64 nTmpStartPos1 = nRelEndPos + 1;
+ uint64 nTmpStartPos2 = m_aaRecords[nPart][i].m_nStartPos;
+ uint64 nTmpEndPos2 = nRelStartPos - 1;
+ m_aaRecords[nPart][i].m_nEndPos = nRelEndPos;
+ m_aaRecords[nPart][i].m_nStartPos = nRelStartPos;
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos1, nTmpEndPos1, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos2, nTmpEndPos2, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_CORRUPTED;
+ }
+ else if (m_aaRecords[nPart][i].m_nStartPos >= nRelStartPos && m_aaRecords[nPart][i].m_nStartPos <= nRelEndPos){
+ // need to split it
+ uint64 nTmpEndPos = m_aaRecords[nPart][i].m_nEndPos;
+ uint64 nTmpStartPos = nRelEndPos + 1;
+ m_aaRecords[nPart][i].m_nEndPos = nRelEndPos;
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos, nTmpEndPos, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_CORRUPTED;
+ }
+ else if (m_aaRecords[nPart][i].m_nEndPos >= nRelStartPos && m_aaRecords[nPart][i].m_nEndPos <= nRelEndPos){
+ // need to split it
+ uint64 nTmpStartPos = m_aaRecords[nPart][i].m_nStartPos;
+ uint64 nTmpEndPos = nRelStartPos - 1;
+ m_aaRecords[nPart][i].m_nStartPos = nRelStartPos;
+ m_aaRecords[nPart].Add(CCBBRecord(nTmpStartPos, nTmpEndPos, m_aaRecords[nPart][i].m_dwIP, m_aaRecords[nPart][i].m_BBRStatus));
+ nDbgVerifiedBytes += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ m_aaRecords[nPart][i].m_BBRStatus = BBR_CORRUPTED;
+ }
+ }
+ }
+ AddDebugLogLine(DLP_HIGH, false, _T("Found and marked %I64u recorded bytes of %I64u as corrupted in the CorruptionBlackBox records"), nDbgVerifiedBytes, (nEndPos-nStartPos)+1);
+}
+
+void CCorruptionBlackBox::EvaluateData(uint16 nPart)
+{
+ CArray aGuiltyClients;
+ for (int i= 0; i < m_aaRecords[nPart].GetCount(); i++)
+ if (m_aaRecords[nPart][i].m_BBRStatus == BBR_CORRUPTED)
+ aGuiltyClients.Add(m_aaRecords[nPart][i].m_dwIP);
+
+ // check if any IPs are already banned, so we can skip the test for those
+ for(int k = 0; k < aGuiltyClients.GetCount();){
+ // remove doubles
+ for(int y = k+1; y < aGuiltyClients.GetCount();){
+ if (aGuiltyClients[k] == aGuiltyClients[y])
+ aGuiltyClients.RemoveAt(y);
+ else
+ y++;
+ }
+ if (theApp.clientlist->IsBannedClient(aGuiltyClients[k])){
+ AddDebugLogLine(DLP_DEFAULT, false, _T("CorruptionBlackBox: Suspicous IP (%s) is already banned, skipping recheck"), ipstr(aGuiltyClients[k]));
+ aGuiltyClients.RemoveAt(k);
+ }
+ else
+ k++;
+ }
+ if (aGuiltyClients.GetCount() > 0){
+ // parse all recorded data for this file to produce a statistic for the involved clients
+
+ // first init arrays for the statistic
+ CArray aDataCorrupt;
+ CArray aDataVerified;
+ aDataCorrupt.SetSize(aGuiltyClients.GetCount());
+ aDataVerified.SetSize(aGuiltyClients.GetCount());
+ for (int j = 0; j < aGuiltyClients.GetCount(); j++)
+ aDataCorrupt[j] = aDataVerified[j] = 0;
+
+ // now the parsing
+ for (int nPart = 0; nPart < m_aaRecords.GetCount(); nPart++){
+ for (int i = 0; i < m_aaRecords[nPart].GetCount(); i++){
+ for(int k = 0; k < aGuiltyClients.GetCount(); k++){
+ if (m_aaRecords[nPart][i].m_dwIP == aGuiltyClients[k]){
+ if (m_aaRecords[nPart][i].m_BBRStatus == BBR_CORRUPTED){
+ // corrupted data records are always counted as at least blocksize or bigger
+ aDataCorrupt[k] += max((m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1, EMBLOCKSIZE);
+ }
+ else if(m_aaRecords[nPart][i].m_BBRStatus == BBR_VERIFIED){
+ aDataVerified[k] += (m_aaRecords[nPart][i].m_nEndPos-m_aaRecords[nPart][i].m_nStartPos)+1;
+ }
+ }
+ }
+ }
+ }
+ for(int k = 0; k < aGuiltyClients.GetCount(); k++){
+ // calculate the percentage of corrupted data for each client and ban
+ // him if the limit is reached
+ int nCorruptPercentage;
+ if ((aDataVerified[k] + aDataCorrupt[k]) > 0)
+ nCorruptPercentage = (int)(((uint64)aDataCorrupt[k]*100)/(aDataVerified[k] + aDataCorrupt[k]));
+ else {
+ AddDebugLogLine(DLP_HIGH, false, _T("CorruptionBlackBox: Programm Error: No records for guilty client found!"));
+ ASSERT( false );
+ nCorruptPercentage = 0;
+ }
+ if ( nCorruptPercentage > CBB_BANTHRESHOLD){
+
+ CUpDownClient* pEvilClient = theApp.clientlist->FindClientByIP(aGuiltyClients[k]);
+ if (pEvilClient != NULL){
+ AddDebugLogLine(DLP_HIGH, false, _T("CorruptionBlackBox: Banning: Found client which send %s of %s corrupted data, %s"), CastItoXBytes(aDataCorrupt[k]), CastItoXBytes((aDataVerified[k] + aDataCorrupt[k])), pEvilClient->DbgGetClientInfo());
+ theApp.clientlist->AddTrackClient(pEvilClient);
+ pEvilClient->Ban(_T("Identified as sender of corrupt data"));
+ }
+ else{
+ AddDebugLogLine(DLP_HIGH, false, _T("CorruptionBlackBox: Banning: Found client which send %s of %s corrupted data, %s"), CastItoXBytes(aDataCorrupt[k]), CastItoXBytes((aDataVerified[k] + aDataCorrupt[k])), ipstr(aGuiltyClients[k]));
+ theApp.clientlist->AddBannedClient(aGuiltyClients[k]);
+ }
+ }
+ else{
+ CUpDownClient* pSuspectClient = theApp.clientlist->FindClientByIP(aGuiltyClients[k]);
+ if (pSuspectClient != NULL){
+ AddDebugLogLine(DLP_DEFAULT, false, _T("CorruptionBlackBox: Reporting: Found client which probably send %s of %s corrupted data, but it is within the acceptable limit, %s"), CastItoXBytes(aDataCorrupt[k]), CastItoXBytes((aDataVerified[k] + aDataCorrupt[k])), pSuspectClient->DbgGetClientInfo());
+ theApp.clientlist->AddTrackClient(pSuspectClient);
+ }
+ else
+ AddDebugLogLine(DLP_DEFAULT, false, _T("CorruptionBlackBox: Reporting: Found client which probably send %s of %s corrupted data, but it is within the acceptable limit, %s"), CastItoXBytes(aDataCorrupt[k]), CastItoXBytes((aDataVerified[k] + aDataCorrupt[k])), ipstr(aGuiltyClients[k]));
+ }
+ }
+ }
+}
diff --git a/CorruptionBlackBox.h b/CorruptionBlackBox.h
new file mode 100644
index 00000000..984cc8b7
--- /dev/null
+++ b/CorruptionBlackBox.h
@@ -0,0 +1,64 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#pragma once
+
+class CUpDownClient;
+
+
+enum EBBRStatus{
+ BBR_NONE = 0,
+ BBR_VERIFIED,
+ BBR_CORRUPTED
+};
+
+
+class CCBBRecord
+{
+public:
+ CCBBRecord(uint64 nStartPos = 0, uint64 nEndPos = 0, uint32 dwIP = 0, EBBRStatus BBRStatus = BBR_NONE);
+ CCBBRecord(const CCBBRecord& cv) { *this = cv; }
+ CCBBRecord& operator=(const CCBBRecord& cv);
+
+ bool Merge(uint64 nStartPos, uint64 nEndPos, uint32 dwIP, EBBRStatus BBRStatus = BBR_NONE);
+ bool CanMerge(uint64 nStartPos, uint64 nEndPos, uint32 dwIP, EBBRStatus BBRStatus = BBR_NONE);
+
+ uint64 m_nStartPos;
+ uint64 m_nEndPos;
+ uint32 m_dwIP;
+ EBBRStatus m_BBRStatus;
+};
+
+typedef CArray CRecordArray;
+
+
+class CCorruptionBlackBox
+{
+public:
+ CCorruptionBlackBox() {}
+ ~CCorruptionBlackBox() {}
+ void Init(EMFileSize nFileSize);
+ void Free();
+ void TransferredData(uint64 nStartPos, uint64 nEndPos, const CUpDownClient* pSender);
+ void VerifiedData(uint64 nStartPos, uint64 nEndPos);
+ void CorruptedData(uint64 nStartPos, uint64 nEndPos);
+ void EvaluateData(uint16 nPart);
+
+
+private:
+ CArray m_aaRecords;
+};
diff --git a/CreditsDlg.cpp b/CreditsDlg.cpp
new file mode 100644
index 00000000..9edf6841
--- /dev/null
+++ b/CreditsDlg.cpp
@@ -0,0 +1,177 @@
+/*
+ You may NOT modify this copyright message. You may add your name, if you
+ changed or improved this code, but you mot not delete any part of this message or
+ make it invisible etc.
+*/
+#include "stdafx.h"
+#include "emule.h"
+#include "CreditsDlg.h"
+#include "CreditsThread.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+// drawable area of the dialog
+#define SCREEN_LEFT 6
+#define SCREEN_TOP 175
+#define SCREEN_RIGHT 345
+#define SCREEN_BOTTOM 296
+
+// button to dismiss dialog
+#define BUTTON_TOP_Y 0
+#define BUTTON_BOTTOM_Y 300
+#define BUTTON_LEFT_X 0
+#define BUTTON_RIGHT_X 350
+
+/////////////////////////////////////////////////////////////////////////////
+// CCreditsDlg dialog
+
+
+CCreditsDlg::CCreditsDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CCreditsDlg::IDD, pParent)
+{
+ //{{AFX_DATA_INIT(CCreditsDlg)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+
+ m_pDC = NULL;
+}
+CCreditsDlg::~CCreditsDlg(){
+ m_imgSplash.DeleteObject();
+}
+
+void CCreditsDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CCreditsDlg)
+ // NOTE: the ClassWizard will add DDX and DDV calls here
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CCreditsDlg, CDialog)
+ ON_WM_LBUTTONDOWN()
+ ON_WM_DESTROY()
+ ON_WM_CREATE()
+ ON_WM_PAINT()
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CCreditsDlg message handlers
+
+void CCreditsDlg::OnLButtonDown(UINT nFlags, CPoint point)
+{
+ CDialog::OnLButtonDown(nFlags, point);
+
+ // see if they clicked on our button to dismiss the dialog
+ if((point.x >= BUTTON_LEFT_X) && (point.x <= BUTTON_RIGHT_X))
+ {
+ if((point.y >= BUTTON_TOP_Y) && (point.y <= BUTTON_BOTTOM_Y))
+ {
+ CDialog::OnOK();
+ return;
+ }
+ }
+
+ PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
+}
+
+BOOL CCreditsDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+ VERIFY( m_imgSplash.Attach(theApp.LoadImage(_T("ABOUT"), _T("JPG"))) );
+ m_rectScreen.SetRect(SCREEN_LEFT, SCREEN_TOP, SCREEN_RIGHT, SCREEN_BOTTOM);
+ StartThread();
+
+ return TRUE;
+}
+
+void CCreditsDlg::OnDestroy()
+{
+ KillThread();
+
+ delete m_pDC;
+ m_pDC = NULL;
+
+ CDialog::OnDestroy();
+}
+
+void CCreditsDlg::StartThread()
+{
+ m_pThread = new CCreditsThread(this, m_pDC->GetSafeHdc(), m_rectScreen);
+
+ if (m_pThread == NULL)
+ return;
+
+ ASSERT_VALID(m_pThread);
+ m_pThread->m_pThreadParams = NULL;
+
+ // Create Thread in a suspended state so we can set the Priority
+ // before it starts getting away from us
+ if (!m_pThread->CreateThread(CREATE_SUSPENDED))
+ {
+ delete m_pThread;
+ m_pThread = NULL;
+ return;
+ }
+
+ // thread priority has been set at idle priority to keep from bogging
+ // down other apps that may also be running.
+ VERIFY(m_pThread->SetThreadPriority(THREAD_PRIORITY_IDLE));
+ // Now the thread can run wild
+ m_pThread->ResumeThread();
+}
+
+void CCreditsDlg::KillThread()
+{
+ // tell thread to shutdown
+ VERIFY(SetEvent(m_pThread->m_hEventKill));
+
+ // wait for thread to finish shutdown
+ VERIFY(WaitForSingleObject(m_pThread->m_hThread, INFINITE) == WAIT_OBJECT_0);
+
+ delete m_pThread;
+ m_pThread = NULL;
+}
+
+int CCreditsDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ if (CDialog::OnCreate(lpCreateStruct) == -1)
+ return -1;
+
+ // m_pDC must be initialized here instead of the constructor
+ // because the HWND isn't created until Create is called.
+ m_pDC = new CClientDC(this);
+
+ return 0;
+}
+
+void CCreditsDlg::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+
+ if (m_imgSplash.GetSafeHandle())
+ {
+ CDC dcMem;
+
+ if (dcMem.CreateCompatibleDC(&dc))
+ {
+ CBitmap* pOldBM = dcMem.SelectObject(&m_imgSplash);
+ BITMAP BM;
+ m_imgSplash.GetBitmap(&BM);
+
+ WINDOWPLACEMENT wp;
+ this->GetWindowPlacement(&wp);
+ wp.rcNormalPosition.right= wp.rcNormalPosition.left+BM.bmWidth;
+ wp.rcNormalPosition.bottom= wp.rcNormalPosition.top+BM.bmHeight;
+ this->SetWindowPlacement(&wp);
+
+ dc.BitBlt(0, 0, BM.bmWidth, BM.bmHeight, &dcMem, 0, 0, SRCCOPY);
+ dcMem.SelectObject(pOldBM);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CreditsDlg.h b/CreditsDlg.h
new file mode 100644
index 00000000..4428150f
--- /dev/null
+++ b/CreditsDlg.h
@@ -0,0 +1,51 @@
+#pragma once
+#include "GDIThread.h"
+#include "resource.h"
+#include "enbitmap.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CCreditsDlg dialog
+
+class CCreditsDlg : public CDialog
+{
+// Construction
+public:
+ void KillThread();
+ void StartThread();
+ CCreditsDlg(CWnd* pParent = NULL); // standard constructor
+ CCreditsDlg::~CCreditsDlg();
+
+ CClientDC* m_pDC;
+ CRect m_rectScreen;
+
+ CGDIThread* m_pThread;
+
+// Dialog Data
+ //{{AFX_DATA(CCreditsDlg)
+ enum { IDD = IDD_ABOUTBOX };
+ // NOTE: the ClassWizard will add data members here
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCreditsDlg)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(CCreditsDlg)
+ afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+ virtual BOOL OnInitDialog();
+ virtual void OnPaint();
+ afx_msg void OnDestroy();
+ afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+private:
+ CBitmap m_imgSplash;
+};
\ No newline at end of file
diff --git a/CreditsThread.cpp b/CreditsThread.cpp
new file mode 100644
index 00000000..72eac27f
--- /dev/null
+++ b/CreditsThread.cpp
@@ -0,0 +1,644 @@
+//this file is part of eMule
+//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include "stdafx.h"
+#include "emule.h"
+#include "CreditsThread.h"
+#include "OtherFunctions.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+// define mask color
+#define MASK_RGB (COLORREF)0xFFFFFF
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_DYNAMIC(CCreditsThread, CGDIThread)
+
+BEGIN_MESSAGE_MAP(CCreditsThread, CGDIThread)
+ //{{AFX_MSG_MAP(CCreditsThread)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+CCreditsThread::CCreditsThread(CWnd* pWnd, HDC hDC, CRect rectScreen)
+ : CGDIThread(pWnd,hDC)
+{
+ m_rectScreen = rectScreen;
+ m_rgnScreen.CreateRectRgnIndirect(m_rectScreen);
+ m_nScrollPos = 0;
+ m_pbmpOldBk = NULL;
+ m_pbmpOldCredits = NULL;
+ m_pbmpOldScreen = NULL;
+ m_pbmpOldMask = NULL;
+ m_nCreditsBmpWidth = 0;
+ m_nCreditsBmpHeight = 0;
+}
+
+CCreditsThread::~CCreditsThread()
+{
+}
+
+BOOL CCreditsThread::InitInstance()
+{
+ InitThreadLocale();
+ BOOL bResult = CGDIThread::InitInstance();
+
+ // NOTE: Because this is a separate thread, we have to delete our GDI objects here (while
+ // the handle maps are still available.)
+ if(m_dcBk.m_hDC != NULL && m_pbmpOldBk != NULL)
+ {
+ m_dcBk.SelectObject(m_pbmpOldBk);
+ m_pbmpOldBk = NULL;
+ m_bmpBk.DeleteObject();
+ }
+
+ if(m_dcScreen.m_hDC != NULL && m_pbmpOldScreen != NULL)
+ {
+ m_dcScreen.SelectObject(m_pbmpOldScreen);
+ m_pbmpOldScreen = NULL;
+ m_bmpScreen.DeleteObject();
+ }
+
+ if(m_dcCredits.m_hDC != NULL && m_pbmpOldCredits != NULL)
+ {
+ m_dcCredits.SelectObject(m_pbmpOldCredits);
+ m_pbmpOldCredits = NULL;
+ m_bmpCredits.DeleteObject();
+ }
+
+ if(m_dcMask.m_hDC != NULL && m_pbmpOldMask != NULL)
+ {
+ m_dcMask.SelectObject(m_pbmpOldMask);
+ m_pbmpOldMask = NULL;
+ m_bmpMask.DeleteObject();
+ }
+
+ // clean up the fonts we created
+ for(int n = 0; n < m_arFonts.GetSize(); n++)
+ {
+ m_arFonts.GetAt(n)->DeleteObject();
+ delete m_arFonts.GetAt(n);
+ }
+ m_arFonts.RemoveAll();
+
+ return bResult;
+}
+
+// wait for vertical retrace
+// makes scrolling smoother, especially at fast speeds
+// NT does not like this at all
+void waitvrt(void)
+{
+ __asm {
+ mov dx,3dah
+ VRT:
+ in al,dx
+ test al,8
+ jnz VRT
+ NoVRT:
+ in al,dx
+ test al,8
+ jz NoVRT
+ }
+}
+
+void CCreditsThread::SingleStep()
+{
+ // if this is our first time, initialize the credits
+ if(m_dcCredits.m_hDC == NULL)
+ {
+ CreateCredits();
+ }
+
+ // track scroll position
+ static int nScrollY = 0;
+
+ // timer variables
+ LARGE_INTEGER nFrequency;
+ LARGE_INTEGER nStart;
+ LARGE_INTEGER nEnd;
+ int nTimeInMilliseconds;
+ BOOL bTimerValid;
+
+ nStart.QuadPart = 0;
+
+ if(!QueryPerformanceFrequency(&nFrequency))
+ {
+ bTimerValid = FALSE;
+ }
+ else
+ {
+ bTimerValid = TRUE;
+
+ // get start time
+ QueryPerformanceCounter(&nStart);
+ }
+
+ CGDIThread::m_csGDILock.Lock();
+ {
+ PaintBk(&m_dcScreen);
+
+ m_dcScreen.BitBlt(0, 0, m_nCreditsBmpWidth, m_nCreditsBmpHeight, &m_dcCredits, 0, nScrollY, SRCINVERT);
+ m_dcScreen.BitBlt(0, 0, m_nCreditsBmpWidth, m_nCreditsBmpHeight, &m_dcMask, 0, nScrollY, SRCAND);
+ m_dcScreen.BitBlt(0, 0, m_nCreditsBmpWidth, m_nCreditsBmpHeight, &m_dcCredits, 0, nScrollY, SRCINVERT);
+
+ // wait for vertical retrace
+ if(m_bWaitVRT) waitvrt();
+
+ m_dc.BitBlt(m_rectScreen.left, m_rectScreen.top, m_rectScreen.Width(), m_rectScreen.Height(), &m_dcScreen, 0, 0, SRCCOPY);
+
+ GdiFlush();
+ }
+ CGDIThread::m_csGDILock.Unlock();
+
+ // continue scrolling
+ nScrollY += m_nScrollInc;
+ if(nScrollY >= m_nCreditsBmpHeight) nScrollY = 0; // scrolling up
+ if(nScrollY < 0) nScrollY = m_nCreditsBmpHeight; // scrolling down
+
+ // delay scrolling by the specified time
+ if(bTimerValid)
+ {
+ QueryPerformanceCounter(&nEnd);
+ nTimeInMilliseconds = (int)((nEnd.QuadPart - nStart.QuadPart) * 1000 / nFrequency.QuadPart);
+
+ if(nTimeInMilliseconds < m_nDelay)
+ {
+ Sleep(m_nDelay - nTimeInMilliseconds);
+ }
+ }
+ else
+ {
+ Sleep(m_nDelay);
+ }
+}
+
+void CCreditsThread::PaintBk(CDC* pDC)
+{
+ //save background the first time
+ if (m_dcBk.m_hDC == NULL)
+ {
+ m_dcBk.CreateCompatibleDC(&m_dc);
+ m_bmpBk.CreateCompatibleBitmap(&m_dc, m_rectScreen.Width(), m_rectScreen.Height());
+ m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
+ m_dcBk.BitBlt(0, 0, m_rectScreen.Width(), m_rectScreen.Height(), &m_dc, m_rectScreen.left, m_rectScreen.top, SRCCOPY);
+ }
+
+ pDC->BitBlt(0, 0, m_rectScreen.Width(), m_rectScreen.Height(), &m_dcBk, 0, 0, SRCCOPY);
+}
+
+void CCreditsThread::CreateCredits()
+{
+ InitFonts();
+ InitColors();
+ InitText();
+
+ m_dc.SelectClipRgn(&m_rgnScreen);
+
+ m_dcScreen.CreateCompatibleDC(&m_dc);
+ m_bmpScreen.CreateCompatibleBitmap(&m_dc, m_rectScreen.Width(), m_rectScreen.Height());
+ m_pbmpOldScreen = m_dcScreen.SelectObject(&m_bmpScreen);
+
+ m_nCreditsBmpWidth = m_rectScreen.Width();
+ m_nCreditsBmpHeight = CalcCreditsHeight();
+
+ m_dcCredits.CreateCompatibleDC(&m_dc);
+ m_bmpCredits.CreateCompatibleBitmap(&m_dc, m_nCreditsBmpWidth, m_nCreditsBmpHeight);
+ m_pbmpOldCredits = m_dcCredits.SelectObject(&m_bmpCredits);
+
+ m_dcCredits.FillSolidRect(0, 0, m_nCreditsBmpWidth, m_nCreditsBmpHeight, MASK_RGB);
+
+ CFont* pOldFont;
+
+ pOldFont = m_dcCredits.SelectObject(m_arFonts.GetAt(0));
+
+ m_dcCredits.SetBkMode(TRANSPARENT);
+
+ int y = 0;
+
+ int nFont;
+ int nColor;
+
+ int nLastFont = -1;
+ int nLastColor = -1;
+
+ int nTextHeight = m_dcCredits.GetTextExtent(_T("Wy")).cy;
+
+ for(int n = 0; n < m_arCredits.GetSize(); n++)
+ {
+ CString sType = m_arCredits.GetAt(n).Left(1);
+
+ if(sType == 'B')
+ {
+ // it's a bitmap
+
+ CBitmap bmp;
+ if(! bmp.LoadBitmap(m_arCredits.GetAt(n).Mid(2)))
+ {
+ CString str;
+ str.Format(_T("Could not find bitmap resource \"%s\". Be sure to assign the bitmap a QUOTED resource name"), m_arCredits.GetAt(n).Mid(2));
+ AfxMessageBox(str);
+ return;
+ }
+
+ BITMAP bmInfo;
+ bmp.GetBitmap(&bmInfo);
+
+ CDC dc;
+ dc.CreateCompatibleDC(&m_dcCredits);
+ CBitmap* pOldBmp = dc.SelectObject(&bmp);
+
+ // draw the bitmap
+ m_dcCredits.BitBlt((m_rectScreen.Width() - bmInfo.bmWidth) / 2, y, bmInfo.bmWidth, bmInfo.bmHeight, &dc, 0, 0, SRCCOPY);
+
+ dc.SelectObject(pOldBmp);
+ bmp.DeleteObject();
+
+ y += bmInfo.bmHeight;
+ }
+ else if(sType == 'S')
+ {
+ // it's a vertical space
+
+ y += _ttoi(m_arCredits.GetAt(n).Mid(2));
+ }
+ else
+ {
+ // it's a text string
+
+ nFont = _ttoi(m_arCredits.GetAt(n).Left(2));
+ nColor = _ttoi(m_arCredits.GetAt(n).Mid(3,2));
+
+ if(nFont != nLastFont)
+ {
+ m_dcCredits.SelectObject(m_arFonts.GetAt(nFont));
+ nTextHeight = m_arFontHeights.GetAt(nFont);
+ }
+
+ if(nColor != nLastColor)
+ {
+ m_dcCredits.SetTextColor(m_arColors.GetAt(nColor));
+ }
+
+ CRect rect(0, y, m_rectScreen.Width(), y + nTextHeight);
+
+ m_dcCredits.DrawText(m_arCredits.GetAt(n).Mid(6), &rect, DT_CENTER);
+
+ y += nTextHeight;
+ }
+ }
+
+ m_dcCredits.SetBkColor(MASK_RGB);
+ m_dcCredits.SelectObject(pOldFont);
+
+ // create the mask bitmap
+ m_dcMask.CreateCompatibleDC(&m_dcScreen);
+ m_bmpMask.CreateBitmap(m_nCreditsBmpWidth, m_nCreditsBmpHeight, 1, 1, NULL);
+
+ // select the mask bitmap into the appropriate dc
+ m_pbmpOldMask = m_dcMask.SelectObject(&m_bmpMask);
+
+ // build mask based on transparent color
+ m_dcMask.BitBlt(0, 0, m_nCreditsBmpWidth, m_nCreditsBmpHeight, &m_dcCredits, 0, 0, SRCCOPY);
+}
+
+void CCreditsThread::InitFonts()
+{
+ // create each font we'll need and add it to the fonts array
+
+ CDC dcMem;
+ dcMem.CreateCompatibleDC(&m_dc);
+ CFont* pOldFont;
+ int nTextHeight;
+
+ LOGFONT lf;
+
+ // font 0
+ // SMALL ARIAL
+ CFont* font0 = new CFont;
+ memset((void*)&lf, 0, sizeof(lf));
+ lf.lfHeight = 12;
+ lf.lfWeight = 500;
+ lf.lfQuality = NONANTIALIASED_QUALITY;
+ _tcscpy(lf.lfFaceName, _T("Arial"));
+ font0->CreateFontIndirect(&lf);
+ m_arFonts.Add(font0);
+
+ pOldFont = dcMem.SelectObject(font0);
+ nTextHeight = dcMem.GetTextExtent(_T("Wy")).cy;
+ m_arFontHeights.Add(nTextHeight);
+
+ // font 1
+ // MEDIUM BOLD ARIAL
+ CFont* font1 = new CFont;
+ memset((void*)&lf, 0, sizeof(lf));
+ lf.lfHeight = 14;
+ lf.lfWeight = 600;
+ lf.lfQuality = NONANTIALIASED_QUALITY;
+ _tcscpy(lf.lfFaceName, _T("Arial"));
+ font1->CreateFontIndirect(&lf);
+ m_arFonts.Add(font1);
+
+ dcMem.SelectObject(font1);
+ nTextHeight = dcMem.GetTextExtent(_T("Wy")).cy;
+ m_arFontHeights.Add(nTextHeight);
+
+ // font 2
+ // LARGE ITALIC HEAVY BOLD TIMES ROMAN
+ CFont* font2 = new CFont;
+ memset((void*)&lf, 0, sizeof(lf));
+ lf.lfHeight = 16;
+ lf.lfWeight = 700;
+ //lf.lfItalic = TRUE;
+ lf.lfQuality = afxIsWin95() ? NONANTIALIASED_QUALITY : ANTIALIASED_QUALITY;
+ _tcscpy(lf.lfFaceName, _T("Arial"));
+ font2->CreateFontIndirect(&lf);
+ m_arFonts.Add(font2);
+
+ dcMem.SelectObject(font2);
+ nTextHeight = dcMem.GetTextExtent(_T("Wy")).cy;
+ m_arFontHeights.Add(nTextHeight);
+
+ // font 3
+ CFont* font3 = new CFont;
+ memset((void*)&lf, 0, sizeof(lf));
+ lf.lfHeight = 25;
+ lf.lfWeight = 900;
+ lf.lfQuality = afxIsWin95() ? NONANTIALIASED_QUALITY : ANTIALIASED_QUALITY;
+ _tcscpy(lf.lfFaceName, _T("Arial"));
+ font3->CreateFontIndirect(&lf);
+ m_arFonts.Add(font3);
+
+ dcMem.SelectObject(font3);
+ nTextHeight = dcMem.GetTextExtent(_T("Wy")).cy;
+ m_arFontHeights.Add(nTextHeight);
+
+ dcMem.SelectObject(pOldFont);
+}
+
+void CCreditsThread::InitColors()
+{
+ // define each color we'll be using
+
+ m_arColors.Add(PALETTERGB(0, 0, 0)); // 0 = BLACK
+ m_arColors.Add(PALETTERGB(90, 90, 90)); // 1 = very dark gray
+ m_arColors.Add(PALETTERGB(128, 128, 128)); // 2 = DARK GRAY
+ m_arColors.Add(PALETTERGB(192, 192, 192)); // 3 = LIGHT GRAY
+ m_arColors.Add(PALETTERGB(200, 50, 50)); // 4 = very light gray
+ m_arColors.Add(PALETTERGB(255, 255, 128)); // 5 white
+ m_arColors.Add(PALETTERGB(0, 0, 128)); // 6 dark blue
+ m_arColors.Add(PALETTERGB(128, 128, 255)); // 7 light blue
+ m_arColors.Add(PALETTERGB(0, 106, 0)); // 8 dark green
+}
+
+void CCreditsThread::InitText()
+{
+ // 1st pair of digits identifies the font to use
+ // 2nd pair of digits identifies the color to use
+ // B = Bitmap
+ // S = Space (moves down the specified number of pixels)
+
+ CString sTmp;
+
+ /*
+ You may NOT modify this copyright message. You may add your name, if you
+ changed or improved this code, but you mot not delete any part of this message,
+ make it invisible etc.
+ */
+
+ // start at the bottom of the screen
+ sTmp.Format(_T("S:%d"), m_rectScreen.Height());
+ m_arCredits.Add(sTmp);
+
+ m_arCredits.Add(_T("03:00:eMule"));
+ sTmp.Format(_T("02:01:Version %s"),theApp.m_strCurVersionLong);
+ m_arCredits.Add(sTmp);
+ m_arCredits.Add(_T("01:06:Copyright (C) 2002-2015 Merkur"));
+ m_arCredits.Add(_T("S:50"));
+ m_arCredits.Add(_T("02:04:Developers"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Ornis"));
+
+ m_arCredits.Add(_T("S:50"));
+
+ m_arCredits.Add(_T("02:04:Tester"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Monk"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Daan"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Elandal"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Frozen_North"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:kayfam"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Khandurian"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Masta2002"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:mrLabr"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Nesi-San"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:SeveredCross"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Skynetman"));
+
+
+ m_arCredits.Add(_T("S:50"));
+ m_arCredits.Add(_T("02:04:Retired Members"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Merkur (the Founder)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:tecxx"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Pach2"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Juanjo"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Barry"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Dirus"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Unknown1"));
+
+
+ m_arCredits.Add(_T("S:50"));
+ m_arCredits.Add(_T("02:04:Thanks to these programmers"));
+ m_arCredits.Add(_T("02:04:for publishing useful codeparts"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Paolo Messina (ResizableDialog class)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:6:PJ Naughter (HttpDownload Dialog)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Jim Connor (Scrolling Credits)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Yury Goltsman (extended Progressbar)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Magomed G. Abdurakhmanov (Hyperlink ctrl)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Arthur Westerman (Titled menu)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Tim Kosse (AsyncSocket-Proxysupport)"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:Keith Rule (Memory DC)"));
+ m_arCredits.Add(_T("S:50"));
+
+ m_arCredits.Add(_T("02:07:And thanks to the following"));
+ m_arCredits.Add(_T("02:07:people for translating eMule"));
+ m_arCredits.Add(_T("02:07:into different languages:"));
+ m_arCredits.Add(_T("S:20"));
+
+
+ m_arCredits.Add(_T("01:06:Arabic: Dody"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Albanian: Besmir"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Basque: TXiKi"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Breton: KAD-Korvigelloù an Drouizig"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Bulgarian: DapKo, Dumper"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Catalan: LeChuck"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Chinese simplyfied: Tim Chen, Qilu T."));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Chinese Traditional: CML, Donlong, Ryan"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Czech: Patejl"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Danish: Tiede, Cirrus, Itchy"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Estonian: Symbio"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Dutch: Mr.Bean"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Finnish: Nikerabbit"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:French: Motte, Emzc, Lalrobin"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Galician: Juan, Emilio R."));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Greek: Michael Papadakis"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Italian: Trevi, FrankyFive"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Japanese: DukeDog, Shinro T."));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Hebrew: Avi-3k"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Hungarian: r0ll3r"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Korean: pooz"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Latvian: Zivs"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Lithuanian: Daan"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Maltese: Reuben"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Norwegian (Bokmal): Iznogood"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Norwegian (Nynorsk): Hallvor"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Polish: Tomasz \"TMouse\" Broniarek"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Portugese: Filipe, Luís Claro"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Portugese Brasilian: DarthMaul,Brasco,Ducho"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Romanian: Dragos"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Russian: T-Mac, BRMAIL"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Slowenian: Rok Kralj"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Spanish Castellano: Azuredraco, Javier L., |_Hell_|"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Swedish: Andre"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Turkish: Burak Y."));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Ukrainian: Kex"));
+ m_arCredits.Add(_T("S:05"));
+ m_arCredits.Add(_T("01:06:Vietnamese: Paul Tran HQ Loc"));
+
+ m_arCredits.Add(_T("S:50"));
+ m_arCredits.Add(_T("02:04:Part of eMule is based on Kademlia:"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("02:04:Peer-to-peer routing based on the XOR metric."));
+ m_arCredits.Add(_T("S:10"));
+ m_arCredits.Add(_T("01:06:Copyright (C) 2002 Petar Maymounkov"));
+ m_arCredits.Add(_T("S:5"));
+ m_arCredits.Add(_T("01:06:http://kademlia.scs.cs.nyu.edu"));
+
+ // pause before repeating
+ m_arCredits.Add(_T("S:100"));
+}
+
+int CCreditsThread::CalcCreditsHeight()
+{
+ int nHeight = 0;
+
+ for(int n = 0; n < m_arCredits.GetSize(); n++)
+ {
+ CString sType = m_arCredits.GetAt(n).Left(1);
+
+ if(sType == 'B')
+ {
+ // it's a bitmap
+
+ CBitmap bmp;
+ if (! bmp.LoadBitmap(m_arCredits.GetAt(n).Mid(2)))
+ {
+ CString str;
+ str.Format(_T("Could not find bitmap resource \"%s\". Be sure to assign the bitmap a QUOTED resource name"), m_arCredits.GetAt(n).Mid(2));
+ AfxMessageBox(str);
+ return -1;
+ }
+
+ BITMAP bmInfo;
+ bmp.GetBitmap(&bmInfo);
+
+ nHeight += bmInfo.bmHeight;
+ }
+ else if(sType == 'S')
+ {
+ // it's a vertical space
+
+ nHeight += _ttoi(m_arCredits.GetAt(n).Mid(2));
+ }
+ else
+ {
+ // it's a text string
+
+ int nFont = _ttoi(m_arCredits.GetAt(n).Left(2));
+ nHeight += m_arFontHeights.GetAt(nFont);
+ }
+ }
+
+ return nHeight;
+}
diff --git a/CreditsThread.h b/CreditsThread.h
new file mode 100644
index 00000000..5648325d
--- /dev/null
+++ b/CreditsThread.h
@@ -0,0 +1,71 @@
+#pragma once
+#include "GDIThread.h"
+
+class CCreditsThread : public CGDIThread
+{
+public:
+ DECLARE_DYNAMIC(CCreditsThread)
+ CCreditsThread(CWnd* pWnd, HDC hDC, CRect rectScreen);
+
+// Attributes
+public:
+ CRect m_rectScreen;
+ CRgn m_rgnScreen;
+
+ int m_nScrollPos;
+
+ // background bitmap
+ CDC m_dcBk;
+ CBitmap m_bmpBk;
+ CBitmap* m_pbmpOldBk;
+
+ // credits bitmap
+ CDC m_dcCredits;
+ CBitmap m_bmpCredits;
+ CBitmap* m_pbmpOldCredits;
+
+ // screen bitmap
+ CDC m_dcScreen;
+ CBitmap m_bmpScreen;
+ CBitmap* m_pbmpOldScreen;
+
+ // mask bitmap
+ CDC m_dcMask;
+ CBitmap m_bmpMask;
+ CBitmap* m_pbmpOldMask;
+
+ int m_nCreditsBmpWidth;
+ int m_nCreditsBmpHeight;
+
+ CArray m_arCredits;
+ CArray m_arColors;
+ CArray m_arFonts;
+ CArray m_arFontHeights;
+
+// Operations
+public:
+ int CalcCreditsHeight();
+ void InitText();
+ void InitColors();
+ void InitFonts();
+ void CreateCredits();
+ virtual BOOL InitInstance();
+ virtual void SingleStep();
+ void PaintBk(CDC* pDC);
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCreditsThread)
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ virtual ~CCreditsThread();
+
+ // Generated message map functions
+ //{{AFX_MSG(CCreditsThread)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ //}}AFX_MSG
+
+ DECLARE_MESSAGE_MAP()
+};
diff --git a/CustomAutoComplete.cpp b/CustomAutoComplete.cpp
new file mode 100644
index 00000000..b8771235
--- /dev/null
+++ b/CustomAutoComplete.cpp
@@ -0,0 +1,373 @@
+//--------------------------------------------------------------------------------------------
+// Name: CCustomAutoComplete (CCUSTOMAUTOCOMPLETE.H)
+// Type: Wrapper class
+// Description: Matches IAutoComplete, IEnumString and the registry (optional) to provide
+// custom auto-complete functionality for EDIT controls - including those in
+// combo boxes - in WTL projects.
+//
+// Author: Klaus H. Probst [kprobst@vbbox.com]
+// URL: http://www.vbbox.com/
+// Copyright: This work is copyright © 2002, Klaus H. Probst
+// Usage: You may use this code as you see fit, provided that you assume all
+// responsibilities for doing so.
+// Distribution: Distribute freely as long as you maintain this notice as part of the
+// file header.
+//
+//
+// Updates: 09-Mai-2003 [bluecow]:
+// - changed original string list code to deal with a LRU list
+// and auto cleanup of list entries according 'iMaxItemCount'.
+// - splitted original code into cpp/h file
+// - removed registry stuff
+// - added file stuff
+// 15-Jan-2004 [Ornis]:
+// - changed adding strings to replace existing ones on a new position
+//
+//
+// Notes:
+//
+//
+// Dependencies:
+//
+// The usual ATL/WTL headers for a normal EXE, plus
+//
+//--------------------------------------------------------------------------------------------
+#include "stdafx.h"
+#include
+#include "CustomAutoComplete.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+CCustomAutoComplete::CCustomAutoComplete()
+{
+ InternalInit();
+}
+
+CCustomAutoComplete::CCustomAutoComplete(const CStringArray& p_sItemList)
+{
+ InternalInit();
+ SetList(p_sItemList);
+}
+
+CCustomAutoComplete::~CCustomAutoComplete()
+{
+ if (m_pac)
+ m_pac.Release();
+}
+
+BOOL CCustomAutoComplete::Bind(HWND p_hWndEdit, DWORD p_dwOptions, LPCTSTR p_lpszFormatString)
+{
+ ATLASSERT(::IsWindow(p_hWndEdit));
+ if ((m_fBound) || (m_pac))
+ return FALSE;
+
+ HRESULT hr = m_pac.CoCreateInstance(CLSID_AutoComplete);
+ if (SUCCEEDED(hr))
+ {
+ if (p_dwOptions){
+ CComQIPtr pAC2(m_pac);
+ if (pAC2){
+ pAC2->SetOptions(p_dwOptions);
+ pAC2.Release();
+ }
+ }
+
+ if (SUCCEEDED(hr = m_pac->Init(p_hWndEdit, this, NULL, p_lpszFormatString)))
+ {
+ m_fBound = TRUE;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+VOID CCustomAutoComplete::Unbind()
+{
+ if (!m_fBound)
+ return;
+ if (m_pac){
+ m_pac.Release();
+ m_fBound = FALSE;
+ }
+}
+
+BOOL CCustomAutoComplete::SetList(const CStringArray& p_sItemList)
+{
+ ATLASSERT(p_sItemList.GetSize() != 0);
+ Clear();
+ m_asList.Append(p_sItemList);
+ return TRUE;
+}
+
+int CCustomAutoComplete::FindItem(const CString& rstr)
+{
+ for (int i = 0; i < m_asList.GetCount(); i++)
+ if (m_asList[i].Compare(rstr) == 0)
+ return i;
+ return -1;
+}
+
+BOOL CCustomAutoComplete::AddItem(const CString& p_sItem, int iPos)
+{
+ if (p_sItem.GetLength() != 0)
+ {
+ int oldpos=FindItem(p_sItem);
+ if (oldpos == -1)
+ {
+ // use a LRU list
+ if (iPos == -1)
+ m_asList.Add(p_sItem);
+ else
+ m_asList.InsertAt(iPos, p_sItem);
+
+ while (m_asList.GetSize() > m_iMaxItemCount)
+ m_asList.RemoveAt(m_asList.GetSize() - 1);
+ return TRUE;
+ } else if (iPos!=-1) {
+ m_asList.RemoveAt(oldpos);
+ if (oldpos m_iMaxItemCount)
+ m_asList.RemoveAt(m_asList.GetSize() - 1);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+int CCustomAutoComplete::GetItemCount()
+{
+ return (int)m_asList.GetCount();
+}
+
+BOOL CCustomAutoComplete::RemoveItem(const CString& p_sItem)
+{
+ if (p_sItem.GetLength() != 0)
+ {
+ int iPos = FindItem(p_sItem);
+ if (iPos != -1)
+ {
+ m_asList.RemoveAt(iPos);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL CCustomAutoComplete::RemoveSelectedItem()
+{
+ if (m_pac == NULL || !IsBound())
+ return FALSE;
+ CComQIPtr pIAutoCompleteDropDown = m_pac;
+ if (!pIAutoCompleteDropDown)
+ return FALSE;
+
+ DWORD dwFlags;
+ LPWSTR pwszItem;
+ if (FAILED(pIAutoCompleteDropDown->GetDropDownStatus(&dwFlags, &pwszItem)))
+ return FALSE;
+ if (dwFlags != ACDD_VISIBLE)
+ return FALSE;
+ if (pwszItem == NULL)
+ return FALSE;
+ CString strItem(pwszItem);
+ CoTaskMemFree(pwszItem);
+
+ return RemoveItem(strItem);
+}
+
+BOOL CCustomAutoComplete::Clear()
+{
+ if (m_asList.GetSize() != 0)
+ {
+ m_asList.RemoveAll();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CCustomAutoComplete::Disable()
+{
+ if ((!m_pac) || (!m_fBound))
+ return FALSE;
+ return SUCCEEDED(EnDisable(FALSE));
+}
+
+BOOL CCustomAutoComplete::Enable(VOID)
+{
+ if ((!m_pac) || (m_fBound))
+ return FALSE;
+ return SUCCEEDED(EnDisable(TRUE));
+}
+
+const CStringArray& CCustomAutoComplete::GetList() const
+{
+ return m_asList;
+}
+
+//
+// IUnknown implementation
+//
+STDMETHODIMP_(ULONG) CCustomAutoComplete::AddRef()
+{
+ ULONG nCount = ::InterlockedIncrement(reinterpret_cast(&m_nRefCount));
+ return nCount;
+}
+
+STDMETHODIMP_(ULONG) CCustomAutoComplete::Release()
+{
+ ULONG nCount = 0;
+ nCount = (ULONG) ::InterlockedDecrement(reinterpret_cast(&m_nRefCount));
+ if (nCount == 0)
+ delete this;
+ return nCount;
+}
+
+STDMETHODIMP CCustomAutoComplete::QueryInterface(REFIID riid, void** ppvObject)
+{
+ HRESULT hr = E_NOINTERFACE;
+ if (ppvObject != NULL)
+ {
+ *ppvObject = NULL;
+
+ if (IID_IUnknown == riid)
+ *ppvObject = static_cast(this);
+ else if (IID_IEnumString == riid)
+ *ppvObject = static_cast(this);
+ if (*ppvObject != NULL)
+ {
+ hr = S_OK;
+ ((LPUNKNOWN)*ppvObject)->AddRef();
+ }
+ }
+ else
+ {
+ hr = E_POINTER;
+ }
+ return hr;
+}
+
+//
+// IEnumString implementation
+//
+STDMETHODIMP CCustomAutoComplete::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
+{
+ HRESULT hr = S_FALSE;
+
+ if (!celt)
+ celt = 1;
+ if (pceltFetched)
+ *pceltFetched = 0;
+ ULONG i;
+ for (i = 0; i < celt; i++)
+ {
+ if (m_nCurrentElement == (ULONG)m_asList.GetSize())
+ break;
+
+ rgelt[i] = (LPWSTR)::CoTaskMemAlloc((ULONG) sizeof(WCHAR) * (m_asList[m_nCurrentElement].GetLength() + 1));
+ wcscpy(rgelt[i], m_asList[m_nCurrentElement]);
+
+ if (pceltFetched)
+ (*pceltFetched)++;
+
+ m_nCurrentElement++;
+ }
+
+ if (i == celt)
+ hr = S_OK;
+
+ return hr;
+}
+
+STDMETHODIMP CCustomAutoComplete::Skip(ULONG celt)
+{
+ m_nCurrentElement += celt;
+ if (m_nCurrentElement > (ULONG)m_asList.GetSize())
+ m_nCurrentElement = 0;
+
+ return S_OK;
+}
+
+STDMETHODIMP CCustomAutoComplete::Reset(void)
+{
+ m_nCurrentElement = 0;
+ return S_OK;
+}
+
+STDMETHODIMP CCustomAutoComplete::Clone(IEnumString** ppenum)
+{
+ if (!ppenum)
+ return E_POINTER;
+
+ CCustomAutoComplete* pnew = new CCustomAutoComplete();
+ pnew->AddRef();
+ *ppenum = pnew;
+ return S_OK;
+}
+
+void CCustomAutoComplete::InternalInit()
+{
+ m_nCurrentElement = 0;
+ m_nRefCount = 0;
+ m_fBound = FALSE;
+ m_iMaxItemCount = 30;
+}
+
+HRESULT CCustomAutoComplete::EnDisable(BOOL p_fEnable)
+{
+ ATLASSERT(m_pac);
+
+ HRESULT hr = m_pac->Enable(p_fEnable);
+ if (SUCCEEDED(hr))
+ m_fBound = p_fEnable;
+ return hr;
+}
+
+BOOL CCustomAutoComplete::LoadList(LPCTSTR pszFileName)
+{
+ FILE* fp = _tfsopen(pszFileName, _T("rb"), _SH_DENYWR);
+ if (fp == NULL)
+ return FALSE;
+
+ // verify Unicode byte-order mark 0xFEFF
+ WORD wBOM = fgetwc(fp);
+ if (wBOM != 0xFEFF){
+ fclose(fp);
+ return FALSE;
+ }
+
+ TCHAR szItem[256];
+ while (_fgetts(szItem, ARRSIZE(szItem), fp) != NULL){
+ CString strItem(szItem);
+ strItem.Trim(_T(" \r\n"));
+ AddItem(strItem, -1);
+ }
+ fclose(fp);
+ return TRUE;
+}
+
+BOOL CCustomAutoComplete::SaveList(LPCTSTR pszFileName)
+{
+ FILE* fp = _tfsopen(pszFileName, _T("wb"), _SH_DENYWR);
+ if (fp == NULL)
+ return FALSE;
+
+ // write Unicode byte-order mark 0xFEFF
+ fputwc(0xFEFF, fp);
+
+ for (int i = 0; i < m_asList.GetCount(); i++)
+ _ftprintf(fp, _T("%s\r\n"), m_asList[i]);
+ fclose(fp);
+ return !ferror(fp);
+}
+
+CString CCustomAutoComplete::GetItem(int pos){
+ if (pos>=m_asList.GetCount()) return NULL;
+ else return m_asList.GetAt(pos);
+}
diff --git a/CustomAutoComplete.h b/CustomAutoComplete.h
new file mode 100644
index 00000000..9d7a85c8
--- /dev/null
+++ b/CustomAutoComplete.h
@@ -0,0 +1,66 @@
+//--------------------------------------------------------------------------------------------
+// Author: Klaus H. Probst [kprobst@vbbox.com]
+//
+//--------------------------------------------------------------------------------------------
+#pragma once
+#include
+#include
+#include
+
+class CCustomAutoComplete :
+ public IEnumString
+{
+private:
+ CStringArray m_asList;
+ CComPtr m_pac;
+
+ ULONG m_nCurrentElement;
+ ULONG m_nRefCount;
+ BOOL m_fBound;
+ int m_iMaxItemCount;
+
+ // Constructors/destructors
+public:
+ CCustomAutoComplete();
+ CCustomAutoComplete(const CStringArray& p_sItemList);
+ ~CCustomAutoComplete();
+
+ // Implementation
+public:
+ BOOL Bind(HWND p_hWndEdit, DWORD p_dwOptions = 0, LPCTSTR p_lpszFormatString = NULL);
+ VOID Unbind();
+ BOOL IsBound() const { return m_fBound; }
+
+ BOOL SetList(const CStringArray& p_sItemList);
+ const CStringArray& GetList() const;
+ int GetItemCount();
+
+ BOOL AddItem(const CString& p_sItem, int iPos);
+ BOOL RemoveItem(const CString& p_sItem);
+ BOOL RemoveSelectedItem();
+ CString GetItem(int pos);
+
+ BOOL Clear();
+ BOOL Disable();
+ BOOL Enable(VOID);
+
+ BOOL LoadList(LPCTSTR pszFileName);
+ BOOL SaveList(LPCTSTR pszFileName);
+
+public:
+ STDMETHOD_(ULONG,AddRef)();
+ STDMETHOD_(ULONG,Release)();
+ STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject);
+
+public:
+ STDMETHOD(Next)(ULONG celt, LPOLESTR* rgelt, ULONG* pceltFetched);
+ STDMETHOD(Skip)(ULONG celt);
+ STDMETHOD(Reset)(void);
+ STDMETHOD(Clone)(IEnumString** ppenum);
+
+ // Internal implementation
+private:
+ void InternalInit();
+ HRESULT EnDisable(BOOL p_fEnable);
+ int FindItem(const CString& rstr);
+};
diff --git a/CxImage/cximage_vc71.sln b/CxImage/cximage_vc71.sln
new file mode 100644
index 00000000..2fb5f6cd
--- /dev/null
+++ b/CxImage/cximage_vc71.sln
@@ -0,0 +1,23 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CxImage", "cximage_vc71.vcproj", "{B437B84A-B4B0-4389-BF5B-7E25FD9DCF3D}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {B437B84A-B4B0-4389-BF5B-7E25FD9DCF3D}.Debug.ActiveCfg = Debug|Win32
+ {B437B84A-B4B0-4389-BF5B-7E25FD9DCF3D}.Debug.Build.0 = Debug|Win32
+ {B437B84A-B4B0-4389-BF5B-7E25FD9DCF3D}.Release.ActiveCfg = Release|Win32
+ {B437B84A-B4B0-4389-BF5B-7E25FD9DCF3D}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/CxImage/cximage_vc71.vcproj b/CxImage/cximage_vc71.vcproj
new file mode 100644
index 00000000..9db363c1
--- /dev/null
+++ b/CxImage/cximage_vc71.vcproj
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CxImage/cximage_vc8.vcproj b/CxImage/cximage_vc8.vcproj
new file mode 100644
index 00000000..9dd99256
--- /dev/null
+++ b/CxImage/cximage_vc8.vcproj
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CxImage/cximage_vc9.vcproj b/CxImage/cximage_vc9.vcproj
new file mode 100644
index 00000000..bc3d3b6b
--- /dev/null
+++ b/CxImage/cximage_vc9.vcproj
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CxImage/license.txt b/CxImage/license.txt
new file mode 100644
index 00000000..8cc0197b
--- /dev/null
+++ b/CxImage/license.txt
@@ -0,0 +1,48 @@
+This copy of the CxImage notices is provided for your convenience. In case of
+any discrepancy between this copy and the notices in the file ximage.h that is
+included in the CxImage distribution, the latter shall prevail.
+
+If you modify CxImage you may insert additional notices immediately following
+this sentence.
+
+--------------------------------------------------------------------------------
+
+COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+
+CxImage version 6.0.0 02/Feb/2008
+
+CxImage : Copyright (C) 2001 - 2008, Davide Pizzolato
+
+Original CImage and CImageIterator implementation are:
+Copyright (C) 1995, Alejandro Aguilar Sierra (asierra(at)servidor(dot)unam(dot)mx)
+
+Covered code is provided under this license on an "as is" basis, without warranty
+of any kind, either expressed or implied, including, without limitation, warranties
+that the covered code is free of defects, merchantable, fit for a particular purpose
+or non-infringing. The entire risk as to the quality and performance of the covered
+code is with you. Should any covered code prove defective in any respect, you (not
+the initial developer or any other contributor) assume the cost of any necessary
+servicing, repair or correction. This disclaimer of warranty constitutes an essential
+part of this license. No use of any covered code is authorized hereunder except under
+this disclaimer.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+source code, or portions hereof, for any purpose, including commercial applications,
+freely and without fee, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source distribution.
+
+--------------------------------------------------------------------------------
+
+Other information: about CxImage, and the latest version, can be found at the
+CxImage home page: http://www.xdp.it
+
+--------------------------------------------------------------------------------
diff --git a/CxImage/xfile.h b/CxImage/xfile.h
new file mode 100644
index 00000000..b64594ee
--- /dev/null
+++ b/CxImage/xfile.h
@@ -0,0 +1,79 @@
+/*
+ * File: xfile.h
+ * Purpose: General Purpose File Class
+ */
+/*
+ --------------------------------------------------------------------------------
+
+ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+
+ CxFile (c) 11/May/2002 Davide Pizzolato - www.xdp.it
+ CxFile version 2.00 23/Aug/2002
+ CxFile version 2.10 16/Dec/2007
+
+ Special thanks to Chris Shearer Cooper for new features, enhancements and bugfixes
+
+ Covered code is provided under this license on an "as is" basis, without warranty
+ of any kind, either expressed or implied, including, without limitation, warranties
+ that the covered code is free of defects, merchantable, fit for a particular purpose
+ or non-infringing. The entire risk as to the quality and performance of the covered
+ code is with you. Should any covered code prove defective in any respect, you (not
+ the initial developer or any other contributor) assume the cost of any necessary
+ servicing, repair or correction. This disclaimer of warranty constitutes an essential
+ part of this license. No use of any covered code is authorized hereunder except under
+ this disclaimer.
+
+ Permission is hereby granted to use, copy, modify, and distribute this
+ source code, or portions hereof, for any purpose, including commercial applications,
+ freely and without fee, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+ --------------------------------------------------------------------------------
+ */
+#if !defined(__xfile_h)
+#define __xfile_h
+
+#if defined (WIN32) || defined (_WIN32_WCE)
+ #include
+#endif
+
+#include
+#include
+
+#include "ximadef.h"
+
+class DLL_EXP CxFile
+{
+public:
+ CxFile(void) { };
+ virtual ~CxFile() { };
+
+ virtual bool Close() = 0;
+ virtual size_t Read(void *buffer, size_t size, size_t count) = 0;
+ virtual size_t Write(const void *buffer, size_t size, size_t count) = 0;
+ virtual bool Seek(long offset, int origin) = 0;
+ virtual long Tell() = 0;
+ virtual long Size() = 0;
+ virtual bool Flush() = 0;
+ virtual bool Eof() = 0;
+ virtual long Error() = 0;
+ virtual bool PutC(unsigned char c)
+ {
+ // Default implementation
+ size_t nWrote = Write(&c, 1, 1);
+ return (bool)(nWrote == 1);
+ }
+ virtual long GetC() = 0;
+ virtual char * GetS(char *string, int n) = 0;
+ virtual long Scanf(const char *format, void* output) = 0;
+};
+
+#endif //__xfile_h
diff --git a/CxImage/ximabmp.cpp b/CxImage/ximabmp.cpp
new file mode 100644
index 00000000..a621ba55
--- /dev/null
+++ b/CxImage/ximabmp.cpp
@@ -0,0 +1,444 @@
+/*
+ * File: ximabmp.cpp
+ * Purpose: Platform Independent BMP Image Class Loader and Writer
+ * 07/Aug/2001 Davide Pizzolato - www.xdp.it
+ * CxImage version 6.0.0 02/Feb/2008
+ */
+
+#include "ximabmp.h"
+
+#if CXIMAGE_SUPPORT_BMP
+
+#include "ximaiter.h"
+
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_ENCODE
+////////////////////////////////////////////////////////////////////////////////
+bool CxImageBMP::Encode(CxFile * hFile)
+{
+
+ if (EncodeSafeCheck(hFile)) return false;
+
+ BITMAPFILEHEADER hdr;
+
+ hdr.bfType = 0x4d42; // 'BM' WINDOWS_BITMAP_SIGNATURE
+ hdr.bfSize = GetSize() + 14 /*sizeof(BITMAPFILEHEADER)*/;
+ hdr.bfReserved1 = hdr.bfReserved2 = 0;
+ hdr.bfOffBits = 14 /*sizeof(BITMAPFILEHEADER)*/ + head.biSize + GetPaletteSize();
+
+ hdr.bfType = ntohs(hdr.bfType);
+ hdr.bfSize = ntohl(hdr.bfSize);
+ hdr.bfOffBits = ntohl(hdr.bfOffBits);
+
+#if CXIMAGE_SUPPORT_ALPHA
+ if (GetNumColors()==0 && AlphaIsValid()){
+
+ BITMAPINFOHEADER infohdr;
+ memcpy(&infohdr,&head,sizeof(BITMAPINFOHEADER));
+ infohdr.biCompression = BI_RGB;
+ infohdr.biBitCount = 32;
+ DWORD dwEffWidth = ((((infohdr.biBitCount * infohdr.biWidth) + 31) / 32) * 4);
+ infohdr.biSizeImage = dwEffWidth * infohdr.biHeight;
+
+ hdr.bfSize = infohdr.biSize + infohdr.biSizeImage + 14 /*sizeof(BITMAPFILEHEADER)*/;
+
+ hdr.bfSize = ntohl(hdr.bfSize);
+ bihtoh(&infohdr);
+
+ // Write the file header
+ hFile->Write(&hdr,min(14,sizeof(BITMAPFILEHEADER)),1);
+ hFile->Write(&infohdr,sizeof(BITMAPINFOHEADER),1);
+ //and DIB+ALPHA interlaced
+ BYTE *srcalpha = AlphaGetPointer();
+ for(long y = 0; y < infohdr.biHeight; ++y){
+ BYTE *srcdib = GetBits(y);
+ for(long x = 0; x < infohdr.biWidth; ++x){
+ hFile->Write(srcdib,3,1);
+ hFile->Write(srcalpha,1,1);
+ srcdib += 3;
+ ++srcalpha;
+ }
+ }
+
+ } else
+#endif //CXIMAGE_SUPPORT_ALPHA
+ {
+ // Write the file header
+ hFile->Write(&hdr,min(14,sizeof(BITMAPFILEHEADER)),1);
+ //copy attributes
+ memcpy(pDib,&head,sizeof(BITMAPINFOHEADER));
+ bihtoh((BITMAPINFOHEADER*)pDib);
+ // Write the DIB header and the pixels
+ hFile->Write(pDib,GetSize(),1);
+ bihtoh((BITMAPINFOHEADER*)pDib);
+ }
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+#endif //CXIMAGE_SUPPORT_ENCODE
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+bool CxImageBMP::Decode(CxFile * hFile)
+{
+ if (hFile == NULL) return false;
+
+ BITMAPFILEHEADER bf;
+ DWORD off = hFile->Tell(); //
+ cx_try {
+ if (hFile->Read(&bf,min(14,sizeof(bf)),1)==0) cx_throw("Not a BMP");
+
+ bf.bfSize = ntohl(bf.bfSize);
+ bf.bfOffBits = ntohl(bf.bfOffBits);
+
+ if (bf.bfType != BFT_BITMAP) { //do we have a RC HEADER?
+ bf.bfOffBits = 0L;
+ hFile->Seek(off,SEEK_SET);
+ }
+
+ BITMAPINFOHEADER bmpHeader;
+ if (!DibReadBitmapInfo(hFile,&bmpHeader)) cx_throw("Error reading BMP info");
+ DWORD dwCompression=bmpHeader.biCompression;
+ DWORD dwBitCount=bmpHeader.biBitCount; //preserve for BI_BITFIELDS compression
+ bool bIsOldBmp = bmpHeader.biSize == sizeof(BITMAPCOREHEADER);
+
+ bool bTopDownDib = bmpHeader.biHeight<0; // check if it's a top-down bitmap
+ if (bTopDownDib) bmpHeader.biHeight=-bmpHeader.biHeight;
+
+ if (info.nEscape == -1) {
+ // Return output dimensions only
+ head.biWidth = bmpHeader.biWidth;
+ head.biHeight = bmpHeader.biHeight;
+ info.dwType = CXIMAGE_FORMAT_BMP;
+ cx_throw("output dimensions returned");
+ }
+
+ if (!Create(bmpHeader.biWidth,bmpHeader.biHeight,bmpHeader.biBitCount,CXIMAGE_FORMAT_BMP))
+ cx_throw("");
+
+ SetXDPI((long) floor(bmpHeader.biXPelsPerMeter * 254.0 / 10000.0 + 0.5));
+ SetYDPI((long) floor(bmpHeader.biYPelsPerMeter * 254.0 / 10000.0 + 0.5));
+
+ if (info.nEscape) cx_throw("Cancelled"); // - cancel decoding
+
+ RGBQUAD *pRgb = GetPalette();
+ if (pRgb){
+ if (bIsOldBmp){
+ // convert a old color table (3 byte entries) to a new
+ // color table (4 byte entries)
+ hFile->Read((void*)pRgb,DibNumColors(&bmpHeader) * sizeof(RGBTRIPLE),1);
+ for (int i=DibNumColors(&head)-1; i>=0; i--){
+ pRgb[i].rgbRed = ((RGBTRIPLE *)pRgb)[i].rgbtRed;
+ pRgb[i].rgbBlue = ((RGBTRIPLE *)pRgb)[i].rgbtBlue;
+ pRgb[i].rgbGreen = ((RGBTRIPLE *)pRgb)[i].rgbtGreen;
+ pRgb[i].rgbReserved = (BYTE)0;
+ }
+ } else {
+ hFile->Read((void*)pRgb,DibNumColors(&bmpHeader) * sizeof(RGBQUAD),1);
+ //force rgbReserved=0, to avoid problems with some WinXp bitmaps
+ for (unsigned int i=0; i - cancel decoding
+
+ switch (dwBitCount) {
+ case 32 :
+ DWORD bfmask[3];
+ if (dwCompression == BI_BITFIELDS)
+ {
+ hFile->Read(bfmask, 12, 1);
+ } else {
+ bfmask[0]=0x00FF0000;
+ bfmask[1]=0x0000FF00;
+ bfmask[2]=0x000000FF;
+ }
+ if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
+ if (dwCompression == BI_BITFIELDS || dwCompression == BI_RGB){
+ long imagesize=4*head.biHeight*head.biWidth;
+ BYTE* buff32=(BYTE*)malloc(imagesize);
+ if (buff32){
+ hFile->Read(buff32, imagesize,1); // read in the pixels
+
+#if CXIMAGE_SUPPORT_ALPHA
+ if (dwCompression == BI_RGB){
+ AlphaCreate();
+ if (AlphaIsValid()){
+ bool bAlphaOk = false;
+ BYTE* p;
+ for (long y=0; ySeek(off + bf.bfOffBits,SEEK_SET);
+ if (dwCompression == BI_RGB){
+ hFile->Read(info.pImage, head.biSizeImage,1); // read in the pixels
+ } else cx_throw("unknown compression");
+ break;
+ case 16 :
+ {
+ DWORD bfmask[3];
+ if (dwCompression == BI_BITFIELDS)
+ {
+ hFile->Read(bfmask, 12, 1);
+ } else {
+ bfmask[0]=0x7C00; bfmask[1]=0x3E0; bfmask[2]=0x1F; //RGB555
+ }
+ // bf.bfOffBits required after the bitfield mask
+ if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
+ // read in the pixels
+ hFile->Read(info.pImage, head.biHeight*((head.biWidth+1)/2)*4,1);
+ // transform into RGB
+ Bitfield2RGB(info.pImage,bfmask[0],bfmask[1],bfmask[2],16);
+ break;
+ }
+ case 8 :
+ case 4 :
+ case 1 :
+ if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET);
+ switch (dwCompression) {
+ case BI_RGB :
+ hFile->Read(info.pImage, head.biSizeImage,1); // read in the pixels
+ break;
+ case BI_RLE4 :
+ {
+ BYTE status_byte = 0;
+ BYTE second_byte = 0;
+ int scanline = 0;
+ int bits = 0;
+ BOOL low_nibble = FALSE;
+ CImageIterator iter(this);
+
+ for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(BYTE), 1);) {
+
+ switch (status_byte) {
+ case RLE_COMMAND :
+ hFile->Read(&status_byte, sizeof(BYTE), 1);
+ switch (status_byte) {
+ case RLE_ENDOFLINE :
+ bits = 0;
+ scanline++;
+ low_nibble = FALSE;
+ break;
+ case RLE_ENDOFBITMAP :
+ bContinue=FALSE;
+ break;
+ case RLE_DELTA :
+ {
+ // read the delta values
+ BYTE delta_x;
+ BYTE delta_y;
+ hFile->Read(&delta_x, sizeof(BYTE), 1);
+ hFile->Read(&delta_y, sizeof(BYTE), 1);
+ // apply them
+ bits += delta_x / 2;
+ scanline += delta_y;
+ break;
+ }
+ default :
+ hFile->Read(&second_byte, sizeof(BYTE), 1);
+ BYTE *sline = iter.GetRow(scanline);
+ for (int i = 0; i < status_byte; i++) {
+ if ((BYTE*)(sline+bits) < (BYTE*)(info.pImage+head.biSizeImage)){
+ if (low_nibble) {
+ if (i&1)
+ *(sline + bits) |= (second_byte & 0x0f);
+ else
+ *(sline + bits) |= (second_byte & 0xf0)>>4;
+ bits++;
+ } else {
+ if (i&1)
+ *(sline + bits) = (BYTE)(second_byte & 0x0f)<<4;
+ else
+ *(sline + bits) = (BYTE)(second_byte & 0xf0);
+ }
+ }
+
+ if ((i & 1) && (i != (status_byte - 1)))
+ hFile->Read(&second_byte, sizeof(BYTE), 1);
+
+ low_nibble = !low_nibble;
+ }
+ if ((((status_byte+1) >> 1) & 1 ) == 1)
+ hFile->Read(&second_byte, sizeof(BYTE), 1);
+ break;
+ };
+ break;
+ default :
+ {
+ BYTE *sline = iter.GetRow(scanline);
+ hFile->Read(&second_byte, sizeof(BYTE), 1);
+ for (unsigned i = 0; i < status_byte; i++) {
+ if ((BYTE*)(sline+bits) < (BYTE*)(info.pImage+head.biSizeImage)){
+ if (low_nibble) {
+ if (i&1)
+ *(sline + bits) |= (second_byte & 0x0f);
+ else
+ *(sline + bits) |= (second_byte & 0xf0)>>4;
+ bits++;
+ } else {
+ if (i&1)
+ *(sline + bits) = (BYTE)(second_byte & 0x0f)<<4;
+ else
+ *(sline + bits) = (BYTE)(second_byte & 0xf0);
+ }
+ }
+ low_nibble = !low_nibble;
+ }
+ }
+ break;
+ };
+ }
+ break;
+ }
+ case BI_RLE8 :
+ {
+ BYTE status_byte = 0;
+ BYTE second_byte = 0;
+ int scanline = 0;
+ int bits = 0;
+ CImageIterator iter(this);
+
+ for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(BYTE), 1);) {
+ switch (status_byte) {
+ case RLE_COMMAND :
+ hFile->Read(&status_byte, sizeof(BYTE), 1);
+ switch (status_byte) {
+ case RLE_ENDOFLINE :
+ bits = 0;
+ scanline++;
+ break;
+ case RLE_ENDOFBITMAP :
+ bContinue=FALSE;
+ break;
+ case RLE_DELTA :
+ {
+ // read the delta values
+ BYTE delta_x;
+ BYTE delta_y;
+ hFile->Read(&delta_x, sizeof(BYTE), 1);
+ hFile->Read(&delta_y, sizeof(BYTE), 1);
+ // apply them
+ bits += delta_x;
+ scanline += delta_y;
+ break;
+ }
+ default :
+ hFile->Read((void *)(iter.GetRow(scanline) + bits), sizeof(BYTE) * status_byte, 1);
+ // align run length to even number of bytes
+ if ((status_byte & 1) == 1)
+ hFile->Read(&second_byte, sizeof(BYTE), 1);
+ bits += status_byte;
+ break;
+ };
+ break;
+ default :
+ BYTE *sline = iter.GetRow(scanline);
+ hFile->Read(&second_byte, sizeof(BYTE), 1);
+ for (unsigned i = 0; i < status_byte; i++) {
+ if ((DWORD)bits
+
+ } cx_catch {
+ if (strcmp(message,"")) strncpy(info.szLastError,message,255);
+ if (info.nEscape == -1 && info.dwType == CXIMAGE_FORMAT_BMP) return true;
+ return false;
+ }
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+/* ReadDibBitmapInfo()
+ *
+ * Will read a file in DIB format and return a global HANDLE to its
+ * BITMAPINFO. This function will work with both "old" and "new"
+ * bitmap formats, but will always return a "new" BITMAPINFO.
+ */
+bool CxImageBMP::DibReadBitmapInfo(CxFile* fh, BITMAPINFOHEADER *pdib)
+{
+ if ((fh==NULL)||(pdib==NULL)) return false;
+
+ if (fh->Read(pdib,sizeof(BITMAPINFOHEADER),1)==0) return false;
+
+ bihtoh(pdib);
+
+ switch (pdib->biSize) // what type of bitmap info is this?
+ {
+ case sizeof(BITMAPINFOHEADER):
+ break;
+
+ case 64: //sizeof(OS2_BMP_HEADER):
+ fh->Seek((long)(64 - sizeof(BITMAPINFOHEADER)),SEEK_CUR);
+ break;
+
+ case sizeof(BITMAPCOREHEADER):
+ {
+ BITMAPCOREHEADER bc = *(BITMAPCOREHEADER*)pdib;
+ pdib->biSize = bc.bcSize;
+ pdib->biWidth = (DWORD)bc.bcWidth;
+ pdib->biHeight = (DWORD)bc.bcHeight;
+ pdib->biPlanes = bc.bcPlanes;
+ pdib->biBitCount = bc.bcBitCount;
+ pdib->biCompression = BI_RGB;
+ pdib->biSizeImage = 0;
+ pdib->biXPelsPerMeter = 0;
+ pdib->biYPelsPerMeter = 0;
+ pdib->biClrUsed = 0;
+ pdib->biClrImportant = 0;
+
+ fh->Seek((long)(sizeof(BITMAPCOREHEADER)-sizeof(BITMAPINFOHEADER)), SEEK_CUR);
+ }
+ break;
+ default:
+ //give a last chance
+ if (pdib->biSize>(sizeof(BITMAPINFOHEADER))&&
+ (pdib->biSizeImage>=(unsigned long)(pdib->biHeight*((((pdib->biBitCount*pdib->biWidth)+31)/32)*4)))&&
+ (pdib->biPlanes==1)&&(pdib->biClrUsed==0))
+ {
+ if (pdib->biCompression==BI_RGB)
+ fh->Seek((long)(pdib->biSize - sizeof(BITMAPINFOHEADER)),SEEK_CUR);
+ break;
+ }
+ return false;
+ }
+
+ FixBitmapInfo(pdib);
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+#endif //CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+#endif // CXIMAGE_SUPPORT_BMP
+////////////////////////////////////////////////////////////////////////////////
diff --git a/CxImage/ximabmp.h b/CxImage/ximabmp.h
new file mode 100644
index 00000000..2bda4feb
--- /dev/null
+++ b/CxImage/ximabmp.h
@@ -0,0 +1,79 @@
+/*
+ * File: ximabmp.h
+ * Purpose: BMP Image Class Loader and Writer
+ */
+/* ==========================================================
+ * CxImageBMP (c) 07/Aug/2001 Davide Pizzolato - www.xdp.it
+ * For conditions of distribution and use, see copyright notice in ximage.h
+ *
+ * Special thanks to Troels Knakkergaard for new features, enhancements and bugfixes
+ *
+ * original CImageBMP and CImageIterator implementation are:
+ * Copyright: (c) 1995, Alejandro Aguilar Sierra
+ *
+ * ==========================================================
+ */
+
+#if !defined(__ximaBMP_h)
+#define __ximaBMP_h
+
+#include "ximage.h"
+
+const int RLE_COMMAND = 0;
+const int RLE_ENDOFLINE = 0;
+const int RLE_ENDOFBITMAP = 1;
+const int RLE_DELTA = 2;
+
+#if !defined(BI_RLE8)
+ #define BI_RLE8 1L
+#endif
+#if !defined(BI_RLE4)
+ #define BI_RLE4 2L
+#endif
+
+#if CXIMAGE_SUPPORT_BMP
+
+class CxImageBMP: public CxImage
+{
+public:
+ CxImageBMP(): CxImage(CXIMAGE_FORMAT_BMP) {};
+
+ bool Decode(CxFile * hFile);
+ bool Decode(FILE *hFile) { CxIOFile file(hFile); return Decode(&file); }
+
+#if CXIMAGE_SUPPORT_ENCODE
+ bool Encode(CxFile * hFile);
+ bool Encode(FILE *hFile) { CxIOFile file(hFile); return Encode(&file); }
+#endif // CXIMAGE_SUPPORT_ENCODE
+
+protected:
+ bool DibReadBitmapInfo(CxFile* fh, BITMAPINFOHEADER *pdib);
+};
+
+#define BFT_ICON 0x4349 /* 'IC' */
+#define BFT_BITMAP 0x4d42 /* 'BM' */
+#define BFT_CURSOR 0x5450 /* 'PT' */
+
+#ifndef WIDTHBYTES
+#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
+#endif
+
+#endif
+
+#define DibWidthBytesN(lpbi, n) (UINT)WIDTHBYTES((UINT)(lpbi)->biWidth * (UINT)(n))
+#define DibWidthBytes(lpbi) DibWidthBytesN(lpbi, (lpbi)->biBitCount)
+
+#define DibSizeImage(lpbi) ((lpbi)->biSizeImage == 0 \
+ ? ((DWORD)(UINT)DibWidthBytes(lpbi) * (DWORD)(UINT)(lpbi)->biHeight) \
+ : (lpbi)->biSizeImage)
+
+#define DibNumColors(lpbi) ((lpbi)->biClrUsed == 0 && (lpbi)->biBitCount <= 8 \
+ ? (int)(1 << (int)(lpbi)->biBitCount) \
+ : (int)(lpbi)->biClrUsed)
+
+#define FixBitmapInfo(lpbi) if ((lpbi)->biSizeImage == 0) \
+ (lpbi)->biSizeImage = DibSizeImage(lpbi); \
+ if ((lpbi)->biClrUsed == 0) \
+ (lpbi)->biClrUsed = DibNumColors(lpbi); \
+
+#endif
diff --git a/CxImage/ximacfg.h b/CxImage/ximacfg.h
new file mode 100644
index 00000000..0b1ece3e
--- /dev/null
+++ b/CxImage/ximacfg.h
@@ -0,0 +1,57 @@
+#if !defined(__ximaCFG_h)
+#define __ximaCFG_h
+
+/////////////////////////////////////////////////////////////////////////////
+// CxImage supported features
+#define CXIMAGE_SUPPORT_ALPHA 1
+#define CXIMAGE_SUPPORT_SELECTION 0
+#define CXIMAGE_SUPPORT_TRANSFORMATION 1
+#define CXIMAGE_SUPPORT_DSP 0
+#define CXIMAGE_SUPPORT_LAYERS 0
+#define CXIMAGE_SUPPORT_INTERPOLATION 0
+
+#define CXIMAGE_SUPPORT_DECODE 1
+#define CXIMAGE_SUPPORT_ENCODE 1 //
+#define CXIMAGE_SUPPORT_WINDOWS 1
+
+/////////////////////////////////////////////////////////////////////////////
+// CxImage supported formats
+#define CXIMAGE_SUPPORT_BMP 1
+#define CXIMAGE_SUPPORT_GIF 0
+#define CXIMAGE_SUPPORT_JPG 0
+#define CXIMAGE_SUPPORT_PNG 1
+#define CXIMAGE_SUPPORT_ICO 0
+#define CXIMAGE_SUPPORT_TIF 0
+#define CXIMAGE_SUPPORT_TGA 0
+#define CXIMAGE_SUPPORT_PCX 0
+#define CXIMAGE_SUPPORT_WBMP 0
+#define CXIMAGE_SUPPORT_WMF 0
+
+#define CXIMAGE_SUPPORT_JP2 0
+#define CXIMAGE_SUPPORT_JPC 0
+#define CXIMAGE_SUPPORT_PGX 0
+#define CXIMAGE_SUPPORT_PNM 0
+#define CXIMAGE_SUPPORT_RAS 0
+
+#define CXIMAGE_SUPPORT_JBG 0 // GPL'd see ../jbig/copying.txt & ../jbig/patents.htm
+
+#define CXIMAGE_SUPPORT_MNG 0
+#define CXIMAGE_SUPPORT_SKA 0
+#define CXIMAGE_SUPPORT_RAW 0
+
+/////////////////////////////////////////////////////////////////////////////
+#define CXIMAGE_MAX_MEMORY 268435456
+
+#define CXIMAGE_DEFAULT_DPI 96
+
+#define CXIMAGE_ERR_NOFILE "null file handler"
+#define CXIMAGE_ERR_NOIMAGE "null image!!!"
+
+#define CXIMAGE_SUPPORT_EXCEPTION_HANDLING 1
+
+/////////////////////////////////////////////////////////////////////////////
+//color to grey mapping
+//#define RGB2GRAY(r,g,b) (((b)*114 + (g)*587 + (r)*299)/1000)
+#define RGB2GRAY(r,g,b) (((b)*117 + (g)*601 + (r)*306) >> 10)
+
+#endif
diff --git a/CxImage/ximadef.h b/CxImage/ximadef.h
new file mode 100644
index 00000000..b388b2b8
--- /dev/null
+++ b/CxImage/ximadef.h
@@ -0,0 +1,206 @@
+#if !defined(__ximadefs_h)
+#define __ximadefs_h
+
+#include "ximacfg.h"
+
+#if defined(_AFXDLL)||defined(_USRDLL)
+ #define DLL_EXP __declspec(dllexport)
+#elif defined(_MSC_VER)&&(_MSC_VER<1200)
+ #define DLL_EXP __declspec(dllimport)
+#else
+ #define DLL_EXP
+#endif
+
+
+#if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
+ #define cx_try try
+ #define cx_throw(message) throw(message)
+ #define cx_catch catch (const char *message)
+#else
+ #define cx_try bool cx_error=false;
+ #define cx_throw(message) {cx_error=true; if(strcmp(message,"")) strncpy(info.szLastError,message,255); goto cx_error_catch;}
+ #define cx_catch cx_error_catch: char message[]=""; if(cx_error)
+#endif
+
+
+#if CXIMAGE_SUPPORT_JP2 || CXIMAGE_SUPPORT_JPC || CXIMAGE_SUPPORT_PGX || CXIMAGE_SUPPORT_PNM || CXIMAGE_SUPPORT_RAS
+ #define CXIMAGE_SUPPORT_JASPER 1
+#else
+ #define CXIMAGE_SUPPORT_JASPER 0
+#endif
+
+#if CXIMAGE_SUPPORT_DSP
+#undef CXIMAGE_SUPPORT_TRANSFORMATION
+ #define CXIMAGE_SUPPORT_TRANSFORMATION 1
+#endif
+
+#if CXIMAGE_SUPPORT_TRANSFORMATION || CXIMAGE_SUPPORT_TIF || CXIMAGE_SUPPORT_TGA || CXIMAGE_SUPPORT_BMP || CXIMAGE_SUPPORT_WINDOWS
+ #define CXIMAGE_SUPPORT_BASICTRANSFORMATIONS 1
+#endif
+
+#if CXIMAGE_SUPPORT_DSP || CXIMAGE_SUPPORT_TRANSFORMATION
+#undef CXIMAGE_SUPPORT_INTERPOLATION
+ #define CXIMAGE_SUPPORT_INTERPOLATION 1
+#endif
+
+#if defined (_WIN32_WCE)
+ #undef CXIMAGE_SUPPORT_WMF
+ #define CXIMAGE_SUPPORT_WMF 0
+#endif
+
+#if !defined(WIN32) && !defined(_WIN32_WCE)
+ #undef CXIMAGE_SUPPORT_WINDOWS
+ #define CXIMAGE_SUPPORT_WINDOWS 0
+#endif
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#ifndef PI
+ #define PI 3.141592653589793f
+#endif
+
+
+#if defined(WIN32) || defined(_WIN32_WCE)
+#include
+#include
+#endif
+
+#include
+#include
+
+#ifdef __BORLANDC__
+
+#ifndef _COMPLEX_DEFINED
+
+typedef struct tagcomplex {
+ double x,y;
+} _complex;
+
+#endif
+
+#define _cabs(c) sqrt(c.x*c.x+c.y*c.y)
+
+#endif
+
+
+#if !defined(WIN32) && !defined(_WIN32_WCE)
+
+#include
+#include
+#include
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+typedef unsigned int UINT;
+
+typedef DWORD COLORREF;
+typedef unsigned int HANDLE;
+typedef void* HRGN;
+
+#ifndef BOOL
+#define BOOL bool
+#endif
+
+#ifndef TRUE
+#define TRUE true
+#endif
+
+#ifndef FALSE
+#define FALSE false
+#endif
+
+#ifndef TCHAR
+#define TCHAR char
+#define _T
+#endif
+
+typedef struct tagRECT
+{
+ long left;
+ long top;
+ long right;
+ long bottom;
+} RECT;
+
+typedef struct tagPOINT
+{
+ long x;
+ long y;
+} POINT;
+
+typedef struct tagRGBQUAD {
+ BYTE rgbBlue;
+ BYTE rgbGreen;
+ BYTE rgbRed;
+ BYTE rgbReserved;
+} RGBQUAD;
+
+#pragma pack(1)
+
+typedef struct tagBITMAPINFOHEADER{
+ DWORD biSize;
+ long biWidth;
+ long biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+ DWORD biCompression;
+ DWORD biSizeImage;
+ long biXPelsPerMeter;
+ long biYPelsPerMeter;
+ DWORD biClrUsed;
+ DWORD biClrImportant;
+} BITMAPINFOHEADER;
+
+typedef struct tagBITMAPFILEHEADER {
+ WORD bfType;
+ DWORD bfSize;
+ WORD bfReserved1;
+ WORD bfReserved2;
+ DWORD bfOffBits;
+} BITMAPFILEHEADER;
+
+typedef struct tagBITMAPCOREHEADER {
+ DWORD bcSize;
+ WORD bcWidth;
+ WORD bcHeight;
+ WORD bcPlanes;
+ WORD bcBitCount;
+} BITMAPCOREHEADER;
+
+typedef struct tagRGBTRIPLE {
+ BYTE rgbtBlue;
+ BYTE rgbtGreen;
+ BYTE rgbtRed;
+} RGBTRIPLE;
+
+#pragma pack()
+
+#define BI_RGB 0L
+#define BI_RLE8 1L
+#define BI_RLE4 2L
+#define BI_BITFIELDS 3L
+
+#define GetRValue(rgb) ((BYTE)(rgb))
+#define GetGValue(rgb) ((BYTE)(((WORD)(rgb)) >> 8))
+#define GetBValue(rgb) ((BYTE)((rgb)>>16))
+#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
+
+#ifndef _COMPLEX_DEFINED
+
+typedef struct tagcomplex {
+ double x,y;
+} _complex;
+
+#endif
+
+#define _cabs(c) sqrt(c.x*c.x+c.y*c.y)
+
+#endif
+
+#endif //__ximadefs
diff --git a/CxImage/ximaenc.cpp b/CxImage/ximaenc.cpp
new file mode 100644
index 00000000..8edfbfcd
--- /dev/null
+++ b/CxImage/ximaenc.cpp
@@ -0,0 +1,1008 @@
+// xImaCodec.cpp : Encode Decode functions
+/* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it
+ * CxImage version 6.0.0 02/Feb/2008
+ */
+
+#include "ximage.h"
+
+#if CXIMAGE_SUPPORT_JPG
+#include "ximajpg.h"
+#endif
+
+#if CXIMAGE_SUPPORT_GIF
+#include "ximagif.h"
+#endif
+
+#if CXIMAGE_SUPPORT_PNG
+#include "ximapng.h"
+#endif
+
+#if CXIMAGE_SUPPORT_MNG
+#include "ximamng.h"
+#endif
+
+#if CXIMAGE_SUPPORT_BMP
+#include "ximabmp.h"
+#endif
+
+#if CXIMAGE_SUPPORT_ICO
+#include "ximaico.h"
+#endif
+
+#if CXIMAGE_SUPPORT_TIF
+#include "ximatif.h"
+#endif
+
+#if CXIMAGE_SUPPORT_TGA
+#include "ximatga.h"
+#endif
+
+#if CXIMAGE_SUPPORT_PCX
+#include "ximapcx.h"
+#endif
+
+#if CXIMAGE_SUPPORT_WBMP
+#include "ximawbmp.h"
+#endif
+
+#if CXIMAGE_SUPPORT_WMF
+#include "ximawmf.h" // - WMF/EMF support
+#endif
+
+#if CXIMAGE_SUPPORT_JBG
+#include "ximajbg.h"
+#endif
+
+#if CXIMAGE_SUPPORT_JASPER
+#include "ximajas.h"
+#endif
+
+#if CXIMAGE_SUPPORT_SKA
+#include "ximaska.h"
+#endif
+
+#if CXIMAGE_SUPPORT_RAW
+#include "ximaraw.h"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_ENCODE
+////////////////////////////////////////////////////////////////////////////////
+bool CxImage::EncodeSafeCheck(CxFile *hFile)
+{
+ if (hFile==NULL) {
+ strcpy(info.szLastError,CXIMAGE_ERR_NOFILE);
+ return true;
+ }
+
+ if (pDib==NULL){
+ strcpy(info.szLastError,CXIMAGE_ERR_NOIMAGE);
+ return true;
+ }
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+//#ifdef WIN32
+//bool CxImage::Save(LPCWSTR filename, DWORD imagetype)
+//{
+// FILE* hFile; //file handle to write the image
+// if ((hFile=_wfopen(filename,L"wb"))==NULL) return false;
+// bool bOK = Encode(hFile,imagetype);
+// fclose(hFile);
+// return bOK;
+//}
+//#endif //WIN32
+////////////////////////////////////////////////////////////////////////////////
+// For UNICODE support: char -> TCHAR
+/**
+ * Saves to disk the image in a specific format.
+ * \param filename: file name
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ */
+bool CxImage::Save(const TCHAR * filename, DWORD imagetype)
+{
+ FILE* hFile; //file handle to write the image
+
+#ifdef WIN32
+ if ((hFile=_tfopen(filename,_T("wb")))==NULL) return false; // For UNICODE support
+#else
+ if ((hFile=fopen(filename,"wb"))==NULL) return false;
+#endif
+
+ bool bOK = Encode(hFile,imagetype);
+ fclose(hFile);
+ return bOK;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Saves to disk the image in a specific format.
+ * \param hFile: file handle, open and enabled for writing.
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ */
+bool CxImage::Encode(FILE *hFile, DWORD imagetype)
+{
+ CxIOFile file(hFile);
+ return Encode(&file,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Saves to memory buffer the image in a specific format.
+ * \param buffer: output memory buffer pointer. Must be NULL,
+ * the function allocates and fill the memory,
+ * the application must free the buffer, see also FreeMemory().
+ * \param size: output memory buffer size.
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ */
+bool CxImage::Encode(BYTE * &buffer, long &size, DWORD imagetype)
+{
+ if (buffer!=NULL){
+ strcpy(info.szLastError,"the buffer must be empty");
+ return false;
+ }
+ CxMemFile file;
+ file.Open();
+ if(Encode(&file,imagetype)){
+ buffer=file.GetBuffer();
+ size=file.Size();
+ return true;
+ }
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Saves to disk the image in a specific format.
+ * \param hFile: file handle (CxMemFile or CxIOFile), with write access.
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ * \sa ENUM_CXIMAGE_FORMATS
+ */
+bool CxImage::Encode(CxFile *hFile, DWORD imagetype)
+{
+
+#if CXIMAGE_SUPPORT_BMP
+
+ if (imagetype==CXIMAGE_FORMAT_BMP){
+ CxImageBMP newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ if (imagetype==CXIMAGE_FORMAT_ICO){
+ CxImageICO newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_TIF
+ if (imagetype==CXIMAGE_FORMAT_TIF){
+ CxImageTIF newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_JPG
+ if (imagetype==CXIMAGE_FORMAT_JPG){
+ CxImageJPG newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ if (imagetype==CXIMAGE_FORMAT_GIF){
+ CxImageGIF newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_PNG
+ if (imagetype==CXIMAGE_FORMAT_PNG){
+ CxImagePNG newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_MNG
+ if (imagetype==CXIMAGE_FORMAT_MNG){
+ CxImageMNG newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_TGA
+ if (imagetype==CXIMAGE_FORMAT_TGA){
+ CxImageTGA newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_PCX
+ if (imagetype==CXIMAGE_FORMAT_PCX){
+ CxImagePCX newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+ if (imagetype==CXIMAGE_FORMAT_WBMP){
+ CxImageWBMP newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_WMF && CXIMAGE_SUPPORT_WINDOWS // - WMF/EMF support
+ if (imagetype==CXIMAGE_FORMAT_WMF){
+ CxImageWMF newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_JBG
+ if (imagetype==CXIMAGE_FORMAT_JBG){
+ CxImageJBG newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_JASPER
+ if (
+ #if CXIMAGE_SUPPORT_JP2
+ imagetype==CXIMAGE_FORMAT_JP2 ||
+ #endif
+ #if CXIMAGE_SUPPORT_JPC
+ imagetype==CXIMAGE_FORMAT_JPC ||
+ #endif
+ #if CXIMAGE_SUPPORT_PGX
+ imagetype==CXIMAGE_FORMAT_PGX ||
+ #endif
+ #if CXIMAGE_SUPPORT_PNM
+ imagetype==CXIMAGE_FORMAT_PNM ||
+ #endif
+ #if CXIMAGE_SUPPORT_RAS
+ imagetype==CXIMAGE_FORMAT_RAS ||
+ #endif
+ false ){
+ CxImageJAS newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile,imagetype)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+
+#if CXIMAGE_SUPPORT_SKA
+ if (imagetype==CXIMAGE_FORMAT_SKA){
+ CxImageSKA newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+
+#if CXIMAGE_SUPPORT_RAW
+ if (imagetype==CXIMAGE_FORMAT_RAW){
+ CxImageRAW newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+
+ strcpy(info.szLastError,"Encode: Unknown format");
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Saves to disk or memory pagecount images, referenced by an array of CxImage pointers.
+ * \param hFile: file handle.
+ * \param pImages: array of CxImage pointers.
+ * \param pagecount: number of images.
+ * \param imagetype: can be CXIMAGE_FORMAT_TIF or CXIMAGE_FORMAT_GIF.
+ * \return true if everything is ok
+ */
+bool CxImage::Encode(FILE * hFile, CxImage ** pImages, int pagecount, DWORD imagetype)
+{
+ CxIOFile file(hFile);
+ return Encode(&file, pImages, pagecount,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Saves to disk or memory pagecount images, referenced by an array of CxImage pointers.
+ * \param hFile: file handle (CxMemFile or CxIOFile), with write access.
+ * \param pImages: array of CxImage pointers.
+ * \param pagecount: number of images.
+ * \param imagetype: can be CXIMAGE_FORMAT_TIF, CXIMAGE_FORMAT_GIF or CXIMAGE_FORMAT_ICO.
+ * \return true if everything is ok
+ */
+bool CxImage::Encode(CxFile * hFile, CxImage ** pImages, int pagecount, DWORD imagetype)
+{
+#if CXIMAGE_SUPPORT_TIF
+ if (imagetype==CXIMAGE_FORMAT_TIF){
+ CxImageTIF newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile,pImages,pagecount)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ if (imagetype==CXIMAGE_FORMAT_GIF){
+ CxImageGIF newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile,pImages,pagecount)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ if (imagetype==CXIMAGE_FORMAT_ICO){
+ CxImageICO newima;
+ newima.Ghost(this);
+ if (newima.Encode(hFile,pImages,pagecount)){
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+ strcpy(info.szLastError,"Multipage Encode, Unsupported operation for this format");
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * exports the image into a RGBA buffer, Useful for OpenGL applications.
+ * \param buffer: output memory buffer pointer. Must be NULL,
+ * the function allocates and fill the memory,
+ * the application must free the buffer, see also FreeMemory().
+ * \param size: output memory buffer size.
+ * \param bFlipY: direction of Y axis. default = false.
+ * \return true if everything is ok
+ */
+bool CxImage::Encode2RGBA(BYTE * &buffer, long &size, bool bFlipY)
+{
+ if (buffer!=NULL){
+ strcpy(info.szLastError,"the buffer must be empty");
+ return false;
+ }
+ CxMemFile file;
+ file.Open();
+ if(Encode2RGBA(&file,bFlipY)){
+ buffer=file.GetBuffer();
+ size=file.Size();
+ return true;
+ }
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * exports the image into a RGBA buffer, Useful for OpenGL applications.
+ * \param hFile: file handle (CxMemFile or CxIOFile), with write access.
+ * \param bFlipY: direction of Y axis. default = false.
+ * \return true if everything is ok
+ */
+bool CxImage::Encode2RGBA(CxFile *hFile, bool bFlipY)
+{
+ if (EncodeSafeCheck(hFile)) return false;
+
+ for (long y1 = 0; y1 < head.biHeight; y1++) {
+ long y = bFlipY ? head.biHeight - 1 - y1 : y1;
+ for(long x = 0; x < head.biWidth; x++) {
+ RGBQUAD color = BlindGetPixelColor(x,y);
+ hFile->PutC(color.rgbRed);
+ hFile->PutC(color.rgbGreen);
+ hFile->PutC(color.rgbBlue);
+ hFile->PutC(color.rgbReserved);
+ }
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+#endif //CXIMAGE_SUPPORT_ENCODE
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+// For UNICODE support: char -> TCHAR
+/**
+ * Reads from disk the image in a specific format.
+ * - If decoding fails using the specified image format,
+ * the function will try the automatic file format recognition.
+ *
+ * \param filename: file name
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ */
+bool CxImage::Load(const TCHAR * filename, DWORD imagetype)
+//bool CxImage::Load(const char * filename, DWORD imagetype)
+{
+ /*FILE* hFile; //file handle to read the image
+ if ((hFile=fopen(filename,"rb"))==NULL) return false;
+ bool bOK = Decode(hFile,imagetype);
+ fclose(hFile);*/
+
+ /* automatic file type recognition */
+ bool bOK = false;
+ if ( GetTypeIndexFromId(imagetype) ){
+ FILE* hFile; //file handle to read the image
+
+#ifdef WIN32
+ if ((hFile=_tfopen(filename,_T("rb")))==NULL) return false; // For UNICODE support
+#else
+ if ((hFile=fopen(filename,"rb"))==NULL) return false;
+#endif
+
+ bOK = Decode(hFile,imagetype);
+ fclose(hFile);
+ if (bOK) return bOK;
+ }
+
+ char szError[256];
+ strcpy(szError,info.szLastError); //save the first error
+
+ // if failed, try automatic recognition of the file...
+ FILE* hFile;
+
+#ifdef WIN32
+ if ((hFile=_tfopen(filename,_T("rb")))==NULL) return false; // For UNICODE support
+#else
+ if ((hFile=fopen(filename,"rb"))==NULL) return false;
+#endif
+
+ bOK = Decode(hFile,CXIMAGE_FORMAT_UNKNOWN);
+ fclose(hFile);
+
+ if (!bOK && imagetype > 0) strcpy(info.szLastError,szError); //restore the first error
+
+ return bOK;
+}
+////////////////////////////////////////////////////////////////////////////////
+#ifdef WIN32
+//bool CxImage::Load(LPCWSTR filename, DWORD imagetype)
+//{
+// /*FILE* hFile; //file handle to read the image
+// if ((hFile=_wfopen(filename, L"rb"))==NULL) return false;
+// bool bOK = Decode(hFile,imagetype);
+// fclose(hFile);*/
+//
+// /* automatic file type recognition */
+// bool bOK = false;
+// if ( GetTypeIndexFromId(imagetype) ){
+// FILE* hFile; //file handle to read the image
+// if ((hFile=_wfopen(filename,L"rb"))==NULL) return false;
+// bOK = Decode(hFile,imagetype);
+// fclose(hFile);
+// if (bOK) return bOK;
+// }
+//
+// char szError[256];
+// strcpy(szError,info.szLastError); //save the first error
+//
+// // if failed, try automatic recognition of the file...
+// FILE* hFile; //file handle to read the image
+// if ((hFile=_wfopen(filename,L"rb"))==NULL) return false;
+// bOK = Decode(hFile,CXIMAGE_FORMAT_UNKNOWN);
+// fclose(hFile);
+//
+// if (!bOK && imagetype > 0) strcpy(info.szLastError,szError); //restore the first error
+//
+// return bOK;
+//}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Loads an image from the application resources.
+ * \param hRes: the resource handle returned by FindResource().
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS.
+ * \param hModule: NULL for internal resource, or external application/DLL hinstance returned by LoadLibray.
+ * \return true if everything is ok
+ */
+bool CxImage::LoadResource(HRSRC hRes, DWORD imagetype, HMODULE hModule)
+{
+ DWORD rsize=SizeofResource(hModule,hRes);
+ HGLOBAL hMem=::LoadResource(hModule,hRes);
+ if (hMem){
+ char* lpVoid=(char*)LockResource(hMem);
+ if (lpVoid){
+ // FILE* fTmp=tmpfile(); doesn't work with network
+ /*char tmpPath[MAX_PATH] = {0};
+ char tmpFile[MAX_PATH] = {0};
+ GetTempPath(MAX_PATH,tmpPath);
+ GetTempFileName(tmpPath,"IMG",0,tmpFile);
+ FILE* fTmp=fopen(tmpFile,"w+b");
+ if (fTmp){
+ fwrite(lpVoid,rsize,1,fTmp);
+ fseek(fTmp,0,SEEK_SET);
+ bool bOK = Decode(fTmp,imagetype);
+ fclose(fTmp);
+ DeleteFile(tmpFile);
+ return bOK;
+ }*/
+
+ CxMemFile fTmp((BYTE*)lpVoid,rsize);
+ return Decode(&fTmp,imagetype);
+ }
+ } else strcpy(info.szLastError,"Unable to load resource!");
+ return false;
+}
+#endif //WIN32
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor from file name, see Load()
+ * \param filename: file name
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ */
+//
+// > filename: file name
+// > imagetype: specify the image format (CXIMAGE_FORMAT_BMP,...)
+// For UNICODE support: char -> TCHAR
+CxImage::CxImage(const TCHAR * filename, DWORD imagetype)
+//CxImage::CxImage(const char * filename, DWORD imagetype)
+{
+ Startup(imagetype);
+ Load(filename,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor from file handle, see Decode()
+ * \param stream: file handle, with read access.
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ */
+CxImage::CxImage(FILE * stream, DWORD imagetype)
+{
+ Startup(imagetype);
+ Decode(stream,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor from CxFile object, see Decode()
+ * \param stream: file handle (CxMemFile or CxIOFile), with read access.
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ */
+CxImage::CxImage(CxFile * stream, DWORD imagetype)
+{
+ Startup(imagetype);
+ Decode(stream,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor from memory buffer, see Decode()
+ * \param buffer: memory buffer
+ * \param size: size of buffer
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ */
+CxImage::CxImage(BYTE * buffer, DWORD size, DWORD imagetype)
+{
+ Startup(imagetype);
+ CxMemFile stream(buffer,size);
+ Decode(&stream,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Loads an image from memory buffer
+ * \param buffer: memory buffer
+ * \param size: size of buffer
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ */
+bool CxImage::Decode(BYTE * buffer, DWORD size, DWORD imagetype)
+{
+ CxMemFile file(buffer,size);
+ return Decode(&file,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Loads an image from file handle.
+ * \param hFile: file handle, with read access.
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ */
+bool CxImage::Decode(FILE *hFile, DWORD imagetype)
+{
+ CxIOFile file(hFile);
+ return Decode(&file,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Loads an image from CxFile object
+ * \param hFile: file handle (CxMemFile or CxIOFile), with read access.
+ * \param imagetype: file format, see ENUM_CXIMAGE_FORMATS
+ * \return true if everything is ok
+ * \sa ENUM_CXIMAGE_FORMATS
+ */
+bool CxImage::Decode(CxFile *hFile, DWORD imagetype)
+{
+ if (hFile == NULL){
+ strcpy(info.szLastError,CXIMAGE_ERR_NOFILE);
+ return false;
+ }
+
+ if (imagetype==CXIMAGE_FORMAT_UNKNOWN){
+ DWORD pos = hFile->Tell();
+#if CXIMAGE_SUPPORT_BMP
+ { CxImageBMP newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_JPG
+ { CxImageJPG newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ { CxImageICO newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ { CxImageGIF newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_PNG
+ { CxImagePNG newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_TIF
+ { CxImageTIF newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_MNG
+ { CxImageMNG newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_TGA
+ { CxImageTGA newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_PCX
+ { CxImagePCX newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+ { CxImageWBMP newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_WMF && CXIMAGE_SUPPORT_WINDOWS
+ { CxImageWMF newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_JBG
+ { CxImageJBG newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_JASPER
+ { CxImageJAS newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_SKA
+ { CxImageSKA newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+#if CXIMAGE_SUPPORT_RAW
+ { CxImageRAW newima; newima.CopyInfo(*this); if (newima.Decode(hFile)) { Transfer(newima); return true; } else hFile->Seek(pos,SEEK_SET); }
+#endif
+ }
+
+#if CXIMAGE_SUPPORT_BMP
+ if (imagetype==CXIMAGE_FORMAT_BMP){
+ CxImageBMP newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_JPG
+ if (imagetype==CXIMAGE_FORMAT_JPG){
+ CxImageJPG newima;
+ newima.CopyInfo(*this); //
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ if (imagetype==CXIMAGE_FORMAT_ICO){
+ CxImageICO newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ info.nNumFrames = newima.info.nNumFrames;
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ if (imagetype==CXIMAGE_FORMAT_GIF){
+ CxImageGIF newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ info.nNumFrames = newima.info.nNumFrames;
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_PNG
+ if (imagetype==CXIMAGE_FORMAT_PNG){
+ CxImagePNG newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_TIF
+ if (imagetype==CXIMAGE_FORMAT_TIF){
+ CxImageTIF newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ info.nNumFrames = newima.info.nNumFrames;
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_MNG
+ if (imagetype==CXIMAGE_FORMAT_MNG){
+ CxImageMNG newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ info.nNumFrames = newima.info.nNumFrames;
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_TGA
+ if (imagetype==CXIMAGE_FORMAT_TGA){
+ CxImageTGA newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_PCX
+ if (imagetype==CXIMAGE_FORMAT_PCX){
+ CxImagePCX newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+ if (imagetype==CXIMAGE_FORMAT_WBMP){
+ CxImageWBMP newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_WMF && CXIMAGE_SUPPORT_WINDOWS // vho - WMF support
+ if (imagetype == CXIMAGE_FORMAT_WMF){
+ CxImageWMF newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_JBG
+ if (imagetype==CXIMAGE_FORMAT_JBG){
+ CxImageJBG newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_JASPER
+ if (
+ #if CXIMAGE_SUPPORT_JP2
+ imagetype==CXIMAGE_FORMAT_JP2 ||
+ #endif
+ #if CXIMAGE_SUPPORT_JPC
+ imagetype==CXIMAGE_FORMAT_JPC ||
+ #endif
+ #if CXIMAGE_SUPPORT_PGX
+ imagetype==CXIMAGE_FORMAT_PGX ||
+ #endif
+ #if CXIMAGE_SUPPORT_PNM
+ imagetype==CXIMAGE_FORMAT_PNM ||
+ #endif
+ #if CXIMAGE_SUPPORT_RAS
+ imagetype==CXIMAGE_FORMAT_RAS ||
+ #endif
+ false ){
+ CxImageJAS newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile,imagetype)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+#if CXIMAGE_SUPPORT_SKA
+ if (imagetype==CXIMAGE_FORMAT_SKA){
+ CxImageSKA newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+
+#if CXIMAGE_SUPPORT_RAW
+ if (imagetype==CXIMAGE_FORMAT_RAW){
+ CxImageRAW newima;
+ newima.CopyInfo(*this);
+ if (newima.Decode(hFile)){
+ Transfer(newima);
+ return true;
+ } else {
+ strcpy(info.szLastError,newima.GetLastError());
+ return false;
+ }
+ }
+#endif
+
+ strcpy(info.szLastError,"Decode: Unknown or wrong format");
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Loads an image from CxFile object
+ * \param hFile: file handle (CxMemFile or CxIOFile), with read access.
+ * \param imagetype: file format, default = 0 (CXIMAGE_FORMAT_UNKNOWN)
+ * \return : if imagetype is not 0, the function returns true when imagetype
+ * matches the file image format. If imagetype is 0, the function returns true
+ * when the file image format is recognized as a supported format.
+ * If the returned value is true, use GetHeight(), GetWidth() or GetType()
+ * to retrieve the basic image information.
+ * \sa ENUM_CXIMAGE_FORMATS
+ */
+bool CxImage::CheckFormat(CxFile * hFile, DWORD imagetype)
+{
+ SetType(CXIMAGE_FORMAT_UNKNOWN);
+ SetEscape(-1);
+
+ if (!Decode(hFile,imagetype))
+ return false;
+
+ if (GetType() == CXIMAGE_FORMAT_UNKNOWN || GetType() != imagetype)
+ return false;
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+bool CxImage::CheckFormat(BYTE * buffer, DWORD size, DWORD imagetype)
+{
+ if (buffer==NULL || size==NULL){
+ strcpy(info.szLastError,"invalid or empty buffer");
+ return false;
+ }
+ CxMemFile file(buffer,size);
+ return CheckFormat(&file,imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+#endif //CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
diff --git a/CxImage/ximage.cpp b/CxImage/ximage.cpp
new file mode 100644
index 00000000..cdf3cc78
--- /dev/null
+++ b/CxImage/ximage.cpp
@@ -0,0 +1,594 @@
+// ximage.cpp : main implementation file
+/* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it
+ * CxImage version 6.0.0 02/Feb/2008
+ */
+
+#include "ximage.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// CxImage
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initialize the internal structures
+ */
+void CxImage::Startup(DWORD imagetype)
+{
+ //init pointers
+ pDib = pSelection = pAlpha = NULL;
+ ppLayers = ppFrames = NULL;
+ //init structures
+ memset(&head,0,sizeof(BITMAPINFOHEADER));
+ memset(&info,0,sizeof(CXIMAGEINFO));
+ //init default attributes
+ info.dwType = imagetype;
+ info.fQuality = 90.0f;
+ info.nAlphaMax = 255;
+ info.nBkgndIndex = -1;
+ info.bEnabled = true;
+ SetXDPI(CXIMAGE_DEFAULT_DPI);
+ SetYDPI(CXIMAGE_DEFAULT_DPI);
+
+ short test = 1;
+ info.bLittleEndianHost = (*((char *) &test) == 1);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Empty image constructor
+ * \param imagetype: (optional) set the image format, see ENUM_CXIMAGE_FORMATS
+ */
+CxImage::CxImage(DWORD imagetype)
+{
+ Startup(imagetype);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Call this function to destroy image pixels, alpha channel, selection and sub layers.
+ * - Attributes are not erased, but IsValid returns false.
+ *
+ * \return true if everything is freed, false if the image is a Ghost
+ */
+bool CxImage::Destroy()
+{
+ //free this only if it's valid and it's not a ghost
+ if (info.pGhost==NULL){
+ if (ppLayers) {
+ for(long n=0; n Use it before Create()
+ */
+void CxImage::CopyInfo(const CxImage &src)
+{
+ if (pDib==NULL) memcpy(&info,&src.info,sizeof(CXIMAGEINFO));
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \sa Copy
+ */
+CxImage& CxImage::operator = (const CxImage& isrc)
+{
+ if (this != &isrc) Copy(isrc);
+ return *this;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes or rebuilds the image.
+ * \param dwWidth: width
+ * \param dwHeight: height
+ * \param wBpp: bit per pixel, can be 1, 4, 8, 24
+ * \param imagetype: (optional) set the image format, see ENUM_CXIMAGE_FORMATS
+ * \return pointer to the internal pDib object; NULL if an error occurs.
+ */
+void* CxImage::Create(DWORD dwWidth, DWORD dwHeight, DWORD wBpp, DWORD imagetype)
+{
+ // destroy the existing image (if any)
+ if (!Destroy())
+ return NULL;
+
+ // prevent further actions if width or height are not vaild
+ if ((dwWidth == 0) || (dwHeight == 0)){
+ strcpy(info.szLastError,"CxImage::Create : width and height must be greater than zero");
+ return NULL;
+ }
+
+ // Make sure bits per pixel is valid
+ if (wBpp <= 1) wBpp = 1;
+ else if (wBpp <= 4) wBpp = 4;
+ else if (wBpp <= 8) wBpp = 8;
+ else wBpp = 24;
+
+ // limit memory requirements (and also a check for bad parameters)
+ if (((dwWidth*dwHeight*wBpp)>>3) > CXIMAGE_MAX_MEMORY ||
+ ((dwWidth*dwHeight*wBpp)/wBpp) != (dwWidth*dwHeight))
+ {
+ strcpy(info.szLastError,"CXIMAGE_MAX_MEMORY exceeded");
+ return NULL;
+ }
+
+ // set the correct bpp value
+ switch (wBpp){
+ case 1:
+ head.biClrUsed = 2; break;
+ case 4:
+ head.biClrUsed = 16; break;
+ case 8:
+ head.biClrUsed = 256; break;
+ default:
+ head.biClrUsed = 0;
+ }
+
+ //set the common image informations
+ info.dwEffWidth = ((((wBpp * dwWidth) + 31) / 32) * 4);
+ info.dwType = imagetype;
+
+ // initialize BITMAPINFOHEADER
+ head.biSize = sizeof(BITMAPINFOHEADER); //
+ head.biWidth = dwWidth; // fill in width from parameter
+ head.biHeight = dwHeight; // fill in height from parameter
+ head.biPlanes = 1; // must be 1
+ head.biBitCount = (WORD)wBpp; // from parameter
+ head.biCompression = BI_RGB;
+ head.biSizeImage = info.dwEffWidth * dwHeight;
+// head.biXPelsPerMeter = 0; See SetXDPI
+// head.biYPelsPerMeter = 0; See SetYDPI
+// head.biClrImportant = 0; See SetClrImportant
+
+ pDib = malloc(GetSize()); // alloc memory block to store our bitmap
+ if (!pDib){
+ strcpy(info.szLastError,"CxImage::Create can't allocate memory");
+ return NULL;
+ }
+
+ //clear the palette
+ RGBQUAD* pal=GetPalette();
+ if (pal) memset(pal,0,GetPaletteSize());
+ //Destroy the existing selection
+#if CXIMAGE_SUPPORT_SELECTION
+ if (pSelection) SelectionDelete();
+#endif //CXIMAGE_SUPPORT_SELECTION
+ //Destroy the existing alpha channel
+#if CXIMAGE_SUPPORT_ALPHA
+ if (pAlpha) AlphaDelete();
+#endif //CXIMAGE_SUPPORT_ALPHA
+
+ // use our bitmap info structure to fill in first part of
+ // our DIB with the BITMAPINFOHEADER
+ BITMAPINFOHEADER* lpbi;
+ lpbi = (BITMAPINFOHEADER*)(pDib);
+ *lpbi = head;
+
+ info.pImage=GetBits();
+
+ return pDib; //return handle to the DIB
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return pointer to the image pixels. USE CAREFULLY
+ */
+BYTE* CxImage::GetBits(DWORD row)
+{
+ if (pDib){
+ if (row) {
+ if (row<(DWORD)head.biHeight){
+ return ((BYTE*)pDib + *(DWORD*)pDib + GetPaletteSize() + (info.dwEffWidth * row));
+ } else {
+ return NULL;
+ }
+ } else {
+ return ((BYTE*)pDib + *(DWORD*)pDib + GetPaletteSize());
+ }
+ }
+ return NULL;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return the size in bytes of the internal pDib object
+ */
+long CxImage::GetSize()
+{
+ return head.biSize + head.biSizeImage + GetPaletteSize();
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks if the coordinates are inside the image
+ * \return true if x and y are both inside the image
+ */
+bool CxImage::IsInside(long x, long y)
+{
+ return (0<=y && y 0) bval = 255;
+ }
+ if (GetBpp() == 4){
+ bval = (BYTE)(17*(0x0F & bval));
+ }
+
+ memset(info.pImage,bval,head.biSizeImage);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Transfers the image from an existing source image. The source becomes empty.
+ * \return true if everything is ok
+ */
+bool CxImage::Transfer(CxImage &from, bool bTransferFrames /*=true*/)
+{
+ if (!Destroy())
+ return false;
+
+ memcpy(&head,&from.head,sizeof(BITMAPINFOHEADER));
+ memcpy(&info,&from.info,sizeof(CXIMAGEINFO));
+
+ pDib = from.pDib;
+ pSelection = from.pSelection;
+ pAlpha = from.pAlpha;
+ ppLayers = from.ppLayers;
+
+ memset(&from.head,0,sizeof(BITMAPINFOHEADER));
+ memset(&from.info,0,sizeof(CXIMAGEINFO));
+ from.pDib = from.pSelection = from.pAlpha = NULL;
+ from.ppLayers = NULL;
+
+ if (bTransferFrames){
+ DestroyFrames();
+ ppFrames = from.ppFrames;
+ from.ppFrames = NULL;
+ }
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * (this) points to the same pDib owned by (*from), the image remains in (*from)
+ * but (this) has the access to the pixels. Use carefully !!!
+ */
+void CxImage::Ghost(const CxImage *from)
+{
+ if (from){
+ memcpy(&head,&from->head,sizeof(BITMAPINFOHEADER));
+ memcpy(&info,&from->info,sizeof(CXIMAGEINFO));
+ pDib = from->pDib;
+ pSelection = from->pSelection;
+ pAlpha = from->pAlpha;
+ ppLayers = from->ppLayers;
+ ppFrames = from->ppFrames;
+ info.pGhost=(CxImage *)from;
+ }
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * turns a 16 or 32 bit bitfield image into a RGB image
+ */
+void CxImage::Bitfield2RGB(BYTE *src, DWORD redmask, DWORD greenmask, DWORD bluemask, BYTE bpp)
+{
+ switch (bpp){
+ case 16:
+ {
+ DWORD ns[3]={0,0,0};
+ // compute the number of shift for each mask
+ for (int i=0;i<16;i++){
+ if ((redmask>>i)&0x01) ns[0]++;
+ if ((greenmask>>i)&0x01) ns[1]++;
+ if ((bluemask>>i)&0x01) ns[2]++;
+ }
+ ns[1]+=ns[0]; ns[2]+=ns[1]; ns[0]=8-ns[0]; ns[1]-=8; ns[2]-=8;
+ // dword aligned width for 16 bit image
+ long effwidth2=(((head.biWidth + 1) / 2) * 4);
+ WORD w;
+ long y2,y3,x2,x3;
+ BYTE *p=info.pImage;
+ // scan the buffer in reverse direction to avoid reallocations
+ for (long y=head.biHeight-1; y>=0; y--){
+ y2=effwidth2*y;
+ y3=info.dwEffWidth*y;
+ for (long x=head.biWidth-1; x>=0; x--){
+ x2 = 2*x+y2;
+ x3 = 3*x+y3;
+ w = (WORD)(src[x2]+256*src[1+x2]);
+ p[ x3]=(BYTE)((w & bluemask)<>ns[1]);
+ p[2+x3]=(BYTE)((w & redmask)>>ns[2]);
+ }
+ }
+ break;
+ }
+ case 32:
+ {
+ DWORD ns[3]={0,0,0};
+ // compute the number of shift for each mask
+ for (int i=8;i<32;i+=8){
+ if (redmask>>i) ns[0]++;
+ if (greenmask>>i) ns[1]++;
+ if (bluemask>>i) ns[2]++;
+ }
+ // dword aligned width for 32 bit image
+ long effwidth4 = head.biWidth * 4;
+ long y4,y3,x4,x3;
+ BYTE *p=info.pImage;
+ // scan the buffer in reverse direction to avoid reallocations
+ for (long y=head.biHeight-1; y>=0; y--){
+ y4=effwidth4*y;
+ y3=info.dwEffWidth*y;
+ for (long x=head.biWidth-1; x>=0; x--){
+ x4 = 4*x+y4;
+ x3 = 3*x+y3;
+ p[ x3]=src[ns[2]+x4];
+ p[1+x3]=src[ns[1]+x4];
+ p[2+x3]=src[ns[0]+x4];
+ }
+ }
+ }
+
+ }
+ return;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Creates an image from a generic buffer
+ * \param pArray: source memory buffer
+ * \param dwWidth: image width
+ * \param dwHeight: image height
+ * \param dwBitsperpixel: can be 1,4,8,24,32
+ * \param dwBytesperline: line alignment, in bytes, for a single row stored in pArray
+ * \param bFlipImage: tune this parameter if the image is upsidedown
+ * \return true if everything is ok
+ */
+bool CxImage::CreateFromArray(BYTE* pArray,DWORD dwWidth,DWORD dwHeight,DWORD dwBitsperpixel, DWORD dwBytesperline, bool bFlipImage)
+{
+ if (pArray==NULL) return false;
+ if (!((dwBitsperpixel==1)||(dwBitsperpixel==4)||(dwBitsperpixel==8)||
+ (dwBitsperpixel==24)||(dwBitsperpixel==32))) return false;
+
+ if (!Create(dwWidth,dwHeight,dwBitsperpixel)) return false;
+
+ if (dwBitsperpixel<24) SetGrayPalette();
+
+#if CXIMAGE_SUPPORT_ALPHA
+ if (dwBitsperpixel==32) AlphaCreate();
+#endif //CXIMAGE_SUPPORT_ALPHA
+
+ BYTE *dst,*src;
+
+ for (DWORD y = 0; yrgbRed,c1->rgbGreen,c1->rgbBlue);
+ int g2 = (int)RGB2GRAY(c2->rgbRed,c2->rgbGreen,c2->rgbBlue);
+
+ return (g1-g2);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * simply calls "if (memblock) free(memblock);".
+ * Useful when calling Encode for a memory buffer,
+ * from a DLL compiled with different memory management options.
+ * CxImage::FreeMemory will use the same memory environment used by Encode.
+ * \author [livecn]
+ */
+void CxImage::FreeMemory(void* memblock)
+{
+ if (memblock)
+ free(memblock);
+}
+
+// eMule 05/09/2008: Moved over from DSP functions to not keep the whole file but only this one function
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a random offset to each pixel in the image
+ * \param radius: maximum pixel displacement
+ * \return true if everything is ok
+ */
+bool CxImage::Jitter(long radius)
+{
+ if (!pDib) return false;
+
+ long nx,ny;
+
+ CxImage tmp(*this);
+ if (!tmp.IsValid()){
+ strcpy(info.szLastError,tmp.GetLastError());
+ return false;
+ }
+
+ long xmin,xmax,ymin,ymax;
+ if (pSelection){
+ xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;
+ ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;
+ } else {
+ xmin = ymin = 0;
+ xmax = head.biWidth; ymax=head.biHeight;
+ }
+
+ for(long y=ymin; y 1000
+#pragma once
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#include "xfile.h"
+#include "xiofile.h"
+#include "xmemfile.h"
+#include "ximadef.h" // adjust some #define
+
+/* see "ximacfg.h" for CxImage configuration options */
+
+/////////////////////////////////////////////////////////////////////////////
+// CxImage formats enumerator
+enum ENUM_CXIMAGE_FORMATS{
+CXIMAGE_FORMAT_UNKNOWN = 0,
+#if CXIMAGE_SUPPORT_BMP
+CXIMAGE_FORMAT_BMP = 1,
+#endif
+#if CXIMAGE_SUPPORT_GIF
+CXIMAGE_FORMAT_GIF = 2,
+#endif
+#if CXIMAGE_SUPPORT_JPG
+CXIMAGE_FORMAT_JPG = 3,
+#endif
+#if CXIMAGE_SUPPORT_PNG
+CXIMAGE_FORMAT_PNG = 4,
+#endif
+#if CXIMAGE_SUPPORT_ICO
+CXIMAGE_FORMAT_ICO = 5,
+#endif
+#if CXIMAGE_SUPPORT_TIF
+CXIMAGE_FORMAT_TIF = 6,
+#endif
+#if CXIMAGE_SUPPORT_TGA
+CXIMAGE_FORMAT_TGA = 7,
+#endif
+#if CXIMAGE_SUPPORT_PCX
+CXIMAGE_FORMAT_PCX = 8,
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+CXIMAGE_FORMAT_WBMP = 9,
+#endif
+#if CXIMAGE_SUPPORT_WMF
+CXIMAGE_FORMAT_WMF = 10,
+#endif
+#if CXIMAGE_SUPPORT_JP2
+CXIMAGE_FORMAT_JP2 = 11,
+#endif
+#if CXIMAGE_SUPPORT_JPC
+CXIMAGE_FORMAT_JPC = 12,
+#endif
+#if CXIMAGE_SUPPORT_PGX
+CXIMAGE_FORMAT_PGX = 13,
+#endif
+#if CXIMAGE_SUPPORT_PNM
+CXIMAGE_FORMAT_PNM = 14,
+#endif
+#if CXIMAGE_SUPPORT_RAS
+CXIMAGE_FORMAT_RAS = 15,
+#endif
+#if CXIMAGE_SUPPORT_JBG
+CXIMAGE_FORMAT_JBG = 16,
+#endif
+#if CXIMAGE_SUPPORT_MNG
+CXIMAGE_FORMAT_MNG = 17,
+#endif
+#if CXIMAGE_SUPPORT_SKA
+CXIMAGE_FORMAT_SKA = 18,
+#endif
+#if CXIMAGE_SUPPORT_RAW
+CXIMAGE_FORMAT_RAW = 19,
+#endif
+CMAX_IMAGE_FORMATS = CXIMAGE_SUPPORT_BMP + CXIMAGE_SUPPORT_GIF + CXIMAGE_SUPPORT_JPG +
+ CXIMAGE_SUPPORT_PNG + CXIMAGE_SUPPORT_MNG + CXIMAGE_SUPPORT_ICO +
+ CXIMAGE_SUPPORT_TIF + CXIMAGE_SUPPORT_TGA + CXIMAGE_SUPPORT_PCX +
+ CXIMAGE_SUPPORT_WBMP+ CXIMAGE_SUPPORT_WMF +
+ CXIMAGE_SUPPORT_JBG + CXIMAGE_SUPPORT_JP2 + CXIMAGE_SUPPORT_JPC +
+ CXIMAGE_SUPPORT_PGX + CXIMAGE_SUPPORT_PNM + CXIMAGE_SUPPORT_RAS +
+ CXIMAGE_SUPPORT_SKA + CXIMAGE_SUPPORT_RAW + 1
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CxImage class
+/////////////////////////////////////////////////////////////////////////////
+class DLL_EXP CxImage
+{
+//extensible information collector
+typedef struct tagCxImageInfo {
+ DWORD dwEffWidth; ///< DWORD aligned scan line width
+ BYTE* pImage; ///< THE IMAGE BITS
+ CxImage* pGhost; ///< if this is a ghost, pGhost points to the body
+ CxImage* pParent; ///< if this is a layer, pParent points to the body
+ DWORD dwType; ///< original image format
+ char szLastError[256]; ///< debugging
+ long nProgress; ///< monitor
+ long nEscape; ///< escape
+ long nBkgndIndex; ///< used for GIF, PNG, MNG
+ RGBQUAD nBkgndColor; ///< used for RGB transparency
+ float fQuality; ///< used for JPEG, JPEG2000 (0.0f ... 100.0f)
+ BYTE nJpegScale; ///< used for JPEG [ignacio]
+ long nFrame; ///< used for TIF, GIF, MNG : actual frame
+ long nNumFrames; ///< used for TIF, GIF, MNG : total number of frames
+ DWORD dwFrameDelay; ///< used for GIF, MNG
+ long xDPI; ///< horizontal resolution
+ long yDPI; ///< vertical resolution
+ RECT rSelectionBox; ///< bounding rectangle
+ BYTE nAlphaMax; ///< max opacity (fade)
+ bool bAlphaPaletteEnabled; ///< true if alpha values in the palette are enabled.
+ bool bEnabled; ///< enables the painting functions
+ long xOffset;
+ long yOffset;
+ DWORD dwCodecOpt[CMAX_IMAGE_FORMATS]; ///< for GIF, TIF : 0=def.1=unc,2=fax3,3=fax4,4=pack,5=jpg
+ RGBQUAD last_c; ///< for GetNearestIndex optimization
+ BYTE last_c_index;
+ bool last_c_isvalid;
+ long nNumLayers;
+ DWORD dwFlags; ///< 0x??00000 = reserved, 0x00??0000 = blend mode, 0x0000???? = layer id - user flags
+ BYTE dispmeth;
+ bool bGetAllFrames;
+ bool bLittleEndianHost;
+
+} CXIMAGEINFO;
+
+public:
+ //public structures
+struct rgb_color { BYTE r,g,b; };
+
+#if CXIMAGE_SUPPORT_WINDOWS
+// text placement data
+// members must be initialized with the InitTextInfo(&this) function.
+typedef struct tagCxTextInfo
+{
+#if defined (_WIN32_WCE)
+ TCHAR text[256]; ///< text for windows CE
+#else
+ TCHAR text[4096]; ///< text (char -> TCHAR for UNICODE [Cesar M])
+#endif
+ LOGFONT lfont; ///< font and codepage data
+ COLORREF fcolor; ///< foreground color
+ long align; ///< DT_CENTER, DT_RIGHT, DT_LEFT aligment for multiline text
+ BYTE smooth; ///< text smoothing option. Default is false.
+ BYTE opaque; ///< text has background or hasn't. Default is true.
+ ///< data for background (ignored if .opaque==FALSE)
+ COLORREF bcolor; ///< background color
+ float b_opacity; ///< opacity value for background between 0.0-1.0 Default is 0. (opaque)
+ BYTE b_outline; ///< outline width for background (zero: no outline)
+ BYTE b_round; ///< rounding radius for background rectangle. % of the height, between 0-50. Default is 10.
+ ///< (backgr. always has a frame: width = 3 pixel + 10% of height by default.)
+} CXTEXTINFO;
+#endif
+
+public:
+/** \addtogroup Constructors */ //@{
+ CxImage(DWORD imagetype = 0);
+ CxImage(DWORD dwWidth, DWORD dwHeight, DWORD wBpp, DWORD imagetype = 0);
+ CxImage(const CxImage &src, bool copypixels = true, bool copyselection = true, bool copyalpha = true);
+ CxImage(const TCHAR * filename, DWORD imagetype); // For UNICODE support: char -> TCHAR
+ CxImage(FILE * stream, DWORD imagetype);
+ CxImage(CxFile * stream, DWORD imagetype);
+ CxImage(BYTE * buffer, DWORD size, DWORD imagetype);
+ virtual ~CxImage() { DestroyFrames(); Destroy(); };
+ CxImage& operator = (const CxImage&);
+//@}
+
+/** \addtogroup Initialization */ //@{
+ void* Create(DWORD dwWidth, DWORD dwHeight, DWORD wBpp, DWORD imagetype = 0);
+ bool Destroy();
+ bool DestroyFrames();
+ void Clear(BYTE bval=0);
+ void Copy(const CxImage &src, bool copypixels = true, bool copyselection = true, bool copyalpha = true);
+ bool Transfer(CxImage &from, bool bTransferFrames = true);
+ bool CreateFromArray(BYTE* pArray,DWORD dwWidth,DWORD dwHeight,DWORD dwBitsperpixel, DWORD dwBytesperline, bool bFlipImage);
+ bool CreateFromMatrix(BYTE** ppMatrix,DWORD dwWidth,DWORD dwHeight,DWORD dwBitsperpixel, DWORD dwBytesperline, bool bFlipImage);
+ void FreeMemory(void* memblock);
+
+ DWORD Dump(BYTE * dst);
+ DWORD UnDump(const BYTE * src);
+ DWORD DumpSize();
+
+//@}
+
+/** \addtogroup Attributes */ //@{
+ long GetSize();
+ BYTE* GetBits(DWORD row = 0);
+ BYTE GetColorType();
+ void* GetDIB() const;
+ DWORD GetHeight() const;
+ DWORD GetWidth() const;
+ DWORD GetEffWidth() const;
+ DWORD GetNumColors() const;
+ WORD GetBpp() const;
+ DWORD GetType() const;
+ const char* GetLastError();
+ static const TCHAR* GetVersion();
+ static const float GetVersionNumber();
+
+ DWORD GetFrameDelay() const;
+ void SetFrameDelay(DWORD d);
+
+ void GetOffset(long *x,long *y);
+ void SetOffset(long x,long y);
+
+ BYTE GetJpegQuality() const;
+ void SetJpegQuality(BYTE q);
+ float GetJpegQualityF() const;
+ void SetJpegQualityF(float q);
+
+ BYTE GetJpegScale() const;
+ void SetJpegScale(BYTE q);
+
+ long GetXDPI() const;
+ long GetYDPI() const;
+ void SetXDPI(long dpi);
+ void SetYDPI(long dpi);
+
+ DWORD GetClrImportant() const;
+ void SetClrImportant(DWORD ncolors = 0);
+
+ long GetProgress() const;
+ long GetEscape() const;
+ void SetProgress(long p);
+ void SetEscape(long i);
+
+ long GetTransIndex() const;
+ RGBQUAD GetTransColor();
+ void SetTransIndex(long idx);
+ void SetTransColor(RGBQUAD rgb);
+ bool IsTransparent() const;
+
+ DWORD GetCodecOption(DWORD imagetype = 0);
+ bool SetCodecOption(DWORD opt, DWORD imagetype = 0);
+
+ DWORD GetFlags() const;
+ void SetFlags(DWORD flags, bool bLockReservedFlags = true);
+
+ BYTE GetDisposalMethod() const;
+ void SetDisposalMethod(BYTE dm);
+
+ bool SetType(DWORD type);
+
+ static DWORD GetNumTypes();
+ static DWORD GetTypeIdFromName(const TCHAR* ext);
+ static DWORD GetTypeIdFromIndex(const DWORD index);
+ static DWORD GetTypeIndexFromId(const DWORD id);
+
+ bool GetRetreiveAllFrames() const;
+ void SetRetreiveAllFrames(bool flag);
+ CxImage * GetFrame(long nFrame) const;
+
+ //void* GetUserData() const {return info.pUserData;}
+ //void SetUserData(void* pUserData) {info.pUserData = pUserData;}
+//@}
+
+/** \addtogroup Palette
+ * These functions have no effects on RGB images and in this case the returned value is always 0.
+ * @{ */
+ bool IsGrayScale();
+ bool IsIndexed() const;
+ bool IsSamePalette(CxImage &img, bool bCheckAlpha = true);
+ DWORD GetPaletteSize();
+ RGBQUAD* GetPalette() const;
+ RGBQUAD GetPaletteColor(BYTE idx);
+ bool GetPaletteColor(BYTE i, BYTE* r, BYTE* g, BYTE* b);
+ BYTE GetNearestIndex(RGBQUAD c);
+ void BlendPalette(COLORREF cr,long perc);
+ void SetGrayPalette();
+ void SetPalette(DWORD n, BYTE *r, BYTE *g, BYTE *b);
+ void SetPalette(RGBQUAD* pPal,DWORD nColors=256);
+ void SetPalette(rgb_color *rgb,DWORD nColors=256);
+ void SetPaletteColor(BYTE idx, BYTE r, BYTE g, BYTE b, BYTE alpha=0);
+ void SetPaletteColor(BYTE idx, RGBQUAD c);
+ void SetPaletteColor(BYTE idx, COLORREF cr);
+ void SwapIndex(BYTE idx1, BYTE idx2);
+ void SwapRGB2BGR();
+ void SetStdPalette();
+//@}
+
+/** \addtogroup Pixel */ //@{
+ bool IsInside(long x, long y);
+ bool IsTransparent(long x,long y);
+ bool GetTransparentMask(CxImage* iDst = 0);
+ RGBQUAD GetPixelColor(long x,long y, bool bGetAlpha = true);
+ BYTE GetPixelIndex(long x,long y);
+ BYTE GetPixelGray(long x, long y);
+ void SetPixelColor(long x,long y,RGBQUAD c, bool bSetAlpha = false);
+ void SetPixelColor(long x,long y,COLORREF cr);
+ void SetPixelIndex(long x,long y,BYTE i);
+ void DrawLine(int StartX, int EndX, int StartY, int EndY, RGBQUAD color, bool bSetAlpha=false);
+ void DrawLine(int StartX, int EndX, int StartY, int EndY, COLORREF cr);
+ void BlendPixelColor(long x,long y,RGBQUAD c, float blend, bool bSetAlpha = false);
+ bool Jitter(long radius=2); // eMule: Moved from DSP functions
+//@}
+
+protected:
+/** \addtogroup Protected */ //@{
+ BYTE BlindGetPixelIndex(const long x,const long y);
+ RGBQUAD BlindGetPixelColor(const long x,const long y, bool bGetAlpha = true);
+ void *BlindGetPixelPointer(const long x,const long y);
+ void BlindSetPixelColor(long x,long y,RGBQUAD c, bool bSetAlpha = false);
+ void BlindSetPixelIndex(long x,long y,BYTE i);
+//@}
+
+public:
+
+#if CXIMAGE_SUPPORT_INTERPOLATION
+/** \addtogroup Interpolation */ //@{
+ //overflow methods:
+ enum OverflowMethod {
+ OM_COLOR=1,
+ OM_BACKGROUND=2,
+ OM_TRANSPARENT=3,
+ OM_WRAP=4,
+ OM_REPEAT=5,
+ OM_MIRROR=6
+ };
+ void OverflowCoordinates(float &x, float &y, OverflowMethod const ofMethod);
+ void OverflowCoordinates(long &x, long &y, OverflowMethod const ofMethod);
+ RGBQUAD GetPixelColorWithOverflow(long x, long y, OverflowMethod const ofMethod=OM_BACKGROUND, RGBQUAD* const rplColor=0);
+ //interpolation methods:
+ enum InterpolationMethod {
+ IM_NEAREST_NEIGHBOUR=1,
+ IM_BILINEAR =2,
+ IM_BSPLINE =3,
+ IM_BICUBIC =4,
+ IM_BICUBIC2 =5,
+ IM_LANCZOS =6,
+ IM_BOX =7,
+ IM_HERMITE =8,
+ IM_HAMMING =9,
+ IM_SINC =10,
+ IM_BLACKMAN =11,
+ IM_BESSEL =12,
+ IM_GAUSSIAN =13,
+ IM_QUADRATIC =14,
+ IM_MITCHELL =15,
+ IM_CATROM =16,
+ IM_HANNING =17,
+ IM_POWER =18
+ };
+ RGBQUAD GetPixelColorInterpolated(float x,float y, InterpolationMethod const inMethod=IM_BILINEAR, OverflowMethod const ofMethod=OM_BACKGROUND, RGBQUAD* const rplColor=0);
+ RGBQUAD GetAreaColorInterpolated(float const xc, float const yc, float const w, float const h, InterpolationMethod const inMethod, OverflowMethod const ofMethod=OM_BACKGROUND, RGBQUAD* const rplColor=0);
+//@}
+
+protected:
+/** \addtogroup Protected */ //@{
+ void AddAveragingCont(RGBQUAD const &color, float const surf, float &rr, float &gg, float &bb, float &aa);
+//@}
+
+/** \addtogroup Kernels */ //@{
+public:
+ static float KernelBSpline(const float x);
+ static float KernelLinear(const float t);
+ static float KernelCubic(const float t);
+ static float KernelGeneralizedCubic(const float t, const float a=-1);
+ static float KernelLanczosSinc(const float t, const float r = 3);
+ static float KernelBox(const float x);
+ static float KernelHermite(const float x);
+ static float KernelHamming(const float x);
+ static float KernelSinc(const float x);
+ static float KernelBlackman(const float x);
+ static float KernelBessel_J1(const float x);
+ static float KernelBessel_P1(const float x);
+ static float KernelBessel_Q1(const float x);
+ static float KernelBessel_Order1(float x);
+ static float KernelBessel(const float x);
+ static float KernelGaussian(const float x);
+ static float KernelQuadratic(const float x);
+ static float KernelMitchell(const float x);
+ static float KernelCatrom(const float x);
+ static float KernelHanning(const float x);
+ static float KernelPower(const float x, const float a = 2);
+//@}
+#endif //CXIMAGE_SUPPORT_INTERPOLATION
+
+/** \addtogroup Painting */ //@{
+#if CXIMAGE_SUPPORT_WINDOWS
+ long Blt(HDC pDC, long x=0, long y=0);
+ HBITMAP MakeBitmap(HDC hdc = NULL, bool bTransparency = false);
+ HICON MakeIcon(HDC hdc = NULL);
+ HANDLE CopyToHandle();
+ bool CreateFromHANDLE(HANDLE hMem); //Windows objects (clipboard)
+ bool CreateFromHBITMAP(HBITMAP hbmp, HPALETTE hpal=0); //Windows resource
+ bool CreateFromHICON(HICON hico);
+ long Draw(HDC hdc, long x=0, long y=0, long cx = -1, long cy = -1, RECT* pClipRect = 0, bool bSmooth = false);
+ long Draw(HDC hdc, const RECT& rect, RECT* pClipRect=NULL, bool bSmooth = false);
+ long Stretch(HDC hdc, long xoffset, long yoffset, long xsize, long ysize, DWORD dwRop = SRCCOPY);
+ long Stretch(HDC hdc, const RECT& rect, DWORD dwRop = SRCCOPY);
+ long Tile(HDC hdc, RECT *rc);
+ long Draw2(HDC hdc, long x=0, long y=0, long cx = -1, long cy = -1);
+ long Draw2(HDC hdc, const RECT& rect);
+ //long DrawString(HDC hdc, long x, long y, const char* text, RGBQUAD color, const char* font, long lSize=0, long lWeight=400, BYTE bItalic=0, BYTE bUnderline=0, bool bSetAlpha=false);
+ long DrawString(HDC hdc, long x, long y, const TCHAR* text, RGBQUAD color, const TCHAR* font, long lSize=0, long lWeight=400, BYTE bItalic=0, BYTE bUnderline=0, bool bSetAlpha=false);
+ // extensions
+ long DrawStringEx(HDC hdc, long x, long y, CXTEXTINFO *pTextType, bool bSetAlpha=false );
+ void InitTextInfo( CXTEXTINFO *txt );
+#endif //CXIMAGE_SUPPORT_WINDOWS
+//@}
+
+ // file operations
+#if CXIMAGE_SUPPORT_DECODE
+/** \addtogroup Decode */ //@{
+#ifdef WIN32
+ //bool Load(LPCWSTR filename, DWORD imagetype=0);
+ bool LoadResource(HRSRC hRes, DWORD imagetype, HMODULE hModule=NULL);
+#endif
+ // For UNICODE support: char -> TCHAR
+ bool Load(const TCHAR* filename, DWORD imagetype=0);
+ //bool Load(const char * filename, DWORD imagetype=0);
+ bool Decode(FILE * hFile, DWORD imagetype);
+ bool Decode(CxFile * hFile, DWORD imagetype);
+ bool Decode(BYTE * buffer, DWORD size, DWORD imagetype);
+
+ bool CheckFormat(CxFile * hFile, DWORD imagetype = 0);
+ bool CheckFormat(BYTE * buffer, DWORD size, DWORD imagetype = 0);
+//@}
+#endif //CXIMAGE_SUPPORT_DECODE
+
+#if CXIMAGE_SUPPORT_ENCODE
+protected:
+/** \addtogroup Protected */ //@{
+ bool EncodeSafeCheck(CxFile *hFile);
+//@}
+
+public:
+/** \addtogroup Encode */ //@{
+#ifdef WIN32
+ //bool Save(LPCWSTR filename, DWORD imagetype=0);
+#endif
+ // For UNICODE support: char -> TCHAR
+ bool Save(const TCHAR* filename, DWORD imagetype);
+ //bool Save(const char * filename, DWORD imagetype=0);
+ bool Encode(FILE * hFile, DWORD imagetype);
+ bool Encode(CxFile * hFile, DWORD imagetype);
+ bool Encode(CxFile * hFile, CxImage ** pImages, int pagecount, DWORD imagetype);
+ bool Encode(FILE *hFile, CxImage ** pImages, int pagecount, DWORD imagetype);
+ bool Encode(BYTE * &buffer, long &size, DWORD imagetype);
+
+ bool Encode2RGBA(CxFile *hFile, bool bFlipY = false);
+ bool Encode2RGBA(BYTE * &buffer, long &size, bool bFlipY = false);
+//@}
+#endif //CXIMAGE_SUPPORT_ENCODE
+
+/** \addtogroup Attributes */ //@{
+ //misc.
+ bool IsValid() const;
+ bool IsEnabled() const;
+ void Enable(bool enable=true);
+
+ // frame operations
+ long GetNumFrames() const;
+ long GetFrame() const;
+ void SetFrame(long nFrame);
+//@}
+
+#if CXIMAGE_SUPPORT_BASICTRANSFORMATIONS
+/** \addtogroup BasicTransformations */ //@{
+ bool GrayScale();
+ bool Flip(bool bFlipSelection = false, bool bFlipAlpha = true);
+ bool Mirror(bool bMirrorSelection = false, bool bMirrorAlpha = true);
+ bool Negative();
+ bool RotateLeft(CxImage* iDst = NULL);
+ bool RotateRight(CxImage* iDst = NULL);
+//@}
+#endif //CXIMAGE_SUPPORT_BASICTRANSFORMATIONS
+
+#if CXIMAGE_SUPPORT_TRANSFORMATION
+/** \addtogroup Transformations */ //@{
+ // image operations
+ bool Rotate(float angle, CxImage* iDst = NULL);
+ bool Rotate2(float angle, CxImage *iDst = NULL, InterpolationMethod inMethod=IM_BILINEAR,
+ OverflowMethod ofMethod=OM_BACKGROUND, RGBQUAD *replColor=0,
+ bool const optimizeRightAngles=true, bool const bKeepOriginalSize=false);
+ bool Rotate180(CxImage* iDst = NULL);
+ bool Resample(long newx, long newy, int mode = 1, CxImage* iDst = NULL);
+ bool Resample2(long newx, long newy, InterpolationMethod const inMethod=IM_BICUBIC2,
+ OverflowMethod const ofMethod=OM_REPEAT, CxImage* const iDst = NULL,
+ bool const disableAveraging=false);
+ bool DecreaseBpp(DWORD nbit, bool errordiffusion, RGBQUAD* ppal = 0, DWORD clrimportant = 0);
+ bool IncreaseBpp(DWORD nbit);
+ bool Dither(long method = 0);
+ bool Crop(long left, long top, long right, long bottom, CxImage* iDst = NULL);
+ bool Crop(const RECT& rect, CxImage* iDst = NULL);
+ bool CropRotatedRectangle( long topx, long topy, long width, long height, float angle, CxImage* iDst = NULL);
+ bool Skew(float xgain, float ygain, long xpivot=0, long ypivot=0, bool bEnableInterpolation = false);
+ bool Expand(long left, long top, long right, long bottom, RGBQUAD canvascolor, CxImage* iDst = 0);
+ bool Expand(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst = 0);
+ bool Thumbnail(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst = 0);
+ bool CircleTransform(int type,long rmax=0,float Koeff=1.0f);
+ bool RedEyeRemove(float strength = 0.8f);
+ bool QIShrink(long newx, long newy, CxImage* const iDst = NULL, bool bChangeBpp = false);
+
+//@}
+#endif //CXIMAGE_SUPPORT_TRANSFORMATION
+
+#if CXIMAGE_SUPPORT_DSP
+/** \addtogroup DSP */ //@{
+ bool Contour();
+ bool HistogramStretch(long method = 0, double threshold = 0);
+ bool HistogramEqualize();
+ bool HistogramNormalize();
+ bool HistogramRoot();
+ bool HistogramLog();
+ long Histogram(long* red, long* green = 0, long* blue = 0, long* gray = 0, long colorspace = 0);
+ bool Repair(float radius = 0.25f, long niterations = 1, long colorspace = 0);
+ bool Combine(CxImage* r,CxImage* g,CxImage* b,CxImage* a, long colorspace = 0);
+ bool FFT2(CxImage* srcReal, CxImage* srcImag, CxImage* dstReal, CxImage* dstImag, long direction = 1, bool bForceFFT = true, bool bMagnitude = true);
+ bool Noise(long level);
+ bool Median(long Ksize=3);
+ bool Gamma(float gamma);
+ bool GammaRGB(float gammaR, float gammaG, float gammaB);
+ bool ShiftRGB(long r, long g, long b);
+ bool Threshold(BYTE level);
+ bool Threshold(CxImage* pThresholdMask);
+ bool Threshold2(BYTE level, bool bDirection, RGBQUAD nBkgndColor, bool bSetAlpha = false);
+ bool Colorize(BYTE hue, BYTE sat, float blend = 1.0f);
+ bool Light(long brightness, long contrast = 0);
+ float Mean();
+ bool Filter(long* kernel, long Ksize, long Kfactor, long Koffset);
+ bool Erode(long Ksize=2);
+ bool Dilate(long Ksize=2);
+ bool Edge(long Ksize=2);
+ void HuePalette(float correction=1);
+ enum ImageOpType { OpAdd, OpAnd, OpXor, OpOr, OpMask, OpSrcCopy, OpDstCopy, OpSub, OpSrcBlend, OpScreen, OpAvg };
+ void Mix(CxImage & imgsrc2, ImageOpType op, long lXOffset = 0, long lYOffset = 0, bool bMixAlpha = false);
+ void MixFrom(CxImage & imagesrc2, long lXOffset, long lYOffset);
+ bool UnsharpMask(float radius = 5.0f, float amount = 0.5f, int threshold = 0);
+ bool Lut(BYTE* pLut);
+ bool Lut(BYTE* pLutR, BYTE* pLutG, BYTE* pLutB, BYTE* pLutA = 0);
+ bool GaussianBlur(float radius = 1.0f, CxImage* iDst = 0);
+ bool TextBlur(BYTE threshold = 100, BYTE decay = 2, BYTE max_depth = 5, bool bBlurHorizontal = true, bool bBlurVertical = true, CxImage* iDst = 0);
+ bool SelectiveBlur(float radius = 1.0f, BYTE threshold = 25, CxImage* iDst = 0);
+ bool Solarize(BYTE level = 128, bool bLinkedChannels = true);
+ bool FloodFill(const long xStart, const long yStart, const RGBQUAD cFillColor, const BYTE tolerance = 0,
+ BYTE nOpacity = 255, const bool bSelectFilledArea = false, const BYTE nSelectionLevel = 255);
+ bool Saturate(const long saturation, const long colorspace = 1);
+ bool ConvertColorSpace(const long dstColorSpace, const long srcColorSpace);
+ int OptimalThreshold(long method = 0, RECT * pBox = 0, CxImage* pContrastMask = 0);
+ bool AdaptiveThreshold(long method = 0, long nBoxSize = 64, CxImage* pContrastMask = 0, long nBias = 0, float fGlobalLocalBalance = 0.5f);
+
+//@}
+
+protected:
+/** \addtogroup Protected */ //@{
+ bool IsPowerof2(long x);
+ bool FFT(int dir,int m,double *x,double *y);
+ bool DFT(int dir,long m,double *x1,double *y1,double *x2,double *y2);
+ bool RepairChannel(CxImage *ch, float radius);
+ //
+ int gen_convolve_matrix (float radius, float **cmatrix_p);
+ float* gen_lookup_table (float *cmatrix, int cmatrix_length);
+ void blur_line (float *ctable, float *cmatrix, int cmatrix_length, BYTE* cur_col, BYTE* dest_col, int y, long bytes);
+ void blur_text (BYTE threshold, BYTE decay, BYTE max_depth, CxImage* iSrc, CxImage* iDst, BYTE bytes);
+//@}
+
+public:
+/** \addtogroup ColorSpace */ //@{
+ bool SplitRGB(CxImage* r,CxImage* g,CxImage* b);
+ bool SplitYUV(CxImage* y,CxImage* u,CxImage* v);
+ bool SplitHSL(CxImage* h,CxImage* s,CxImage* l);
+ bool SplitYIQ(CxImage* y,CxImage* i,CxImage* q);
+ bool SplitXYZ(CxImage* x,CxImage* y,CxImage* z);
+ bool SplitCMYK(CxImage* c,CxImage* m,CxImage* y,CxImage* k);
+ static RGBQUAD HSLtoRGB(COLORREF cHSLColor);
+ static RGBQUAD RGBtoHSL(RGBQUAD lRGBColor);
+ static RGBQUAD HSLtoRGB(RGBQUAD lHSLColor);
+ static RGBQUAD YUVtoRGB(RGBQUAD lYUVColor);
+ static RGBQUAD RGBtoYUV(RGBQUAD lRGBColor);
+ static RGBQUAD YIQtoRGB(RGBQUAD lYIQColor);
+ static RGBQUAD RGBtoYIQ(RGBQUAD lRGBColor);
+ static RGBQUAD XYZtoRGB(RGBQUAD lXYZColor);
+ static RGBQUAD RGBtoXYZ(RGBQUAD lRGBColor);
+#endif //CXIMAGE_SUPPORT_DSP
+ static RGBQUAD RGBtoRGBQUAD(COLORREF cr);
+ static COLORREF RGBQUADtoRGB (RGBQUAD c);
+//@}
+
+#if CXIMAGE_SUPPORT_SELECTION
+/** \addtogroup Selection */ //@{
+ bool SelectionClear(BYTE level = 0);
+ bool SelectionCreate();
+ bool SelectionDelete();
+ bool SelectionInvert();
+ bool SelectionMirror();
+ bool SelectionFlip();
+ bool SelectionAddRect(RECT r, BYTE level = 255);
+ bool SelectionAddEllipse(RECT r, BYTE level = 255);
+ bool SelectionAddPolygon(POINT *points, long npoints, BYTE level = 255);
+ bool SelectionAddColor(RGBQUAD c, BYTE level = 255);
+ bool SelectionAddPixel(long x, long y, BYTE level = 255);
+ bool SelectionCopy(CxImage &from);
+ bool SelectionIsInside(long x, long y);
+ bool SelectionIsValid();
+ void SelectionGetBox(RECT& r);
+ bool SelectionToHRGN(HRGN& region);
+ bool SelectionSplit(CxImage *dest);
+ BYTE SelectionGet(const long x,const long y);
+ bool SelectionSet(CxImage &from);
+ void SelectionRebuildBox();
+ BYTE* SelectionGetPointer(const long x = 0,const long y = 0);
+//@}
+
+protected:
+/** \addtogroup Protected */ //@{
+ bool BlindSelectionIsInside(long x, long y);
+ BYTE BlindSelectionGet(const long x,const long y);
+ void SelectionSet(const long x,const long y,const BYTE level);
+//@}
+
+public:
+
+#endif //CXIMAGE_SUPPORT_SELECTION
+
+#if CXIMAGE_SUPPORT_ALPHA
+/** \addtogroup Alpha */ //@{
+ void AlphaClear();
+ bool AlphaCreate();
+ void AlphaDelete();
+ void AlphaInvert();
+ bool AlphaMirror();
+ bool AlphaFlip();
+ bool AlphaCopy(CxImage &from);
+ bool AlphaSplit(CxImage *dest);
+ void AlphaStrip();
+ void AlphaSet(BYTE level);
+ bool AlphaSet(CxImage &from);
+ void AlphaSet(const long x,const long y,const BYTE level);
+ BYTE AlphaGet(const long x,const long y);
+ BYTE AlphaGetMax() const;
+ void AlphaSetMax(BYTE nAlphaMax);
+ bool AlphaIsValid();
+ BYTE* AlphaGetPointer(const long x = 0,const long y = 0);
+ bool AlphaFromTransparency();
+
+ void AlphaPaletteClear();
+ void AlphaPaletteEnable(bool enable=true);
+ bool AlphaPaletteIsEnabled();
+ bool AlphaPaletteIsValid();
+ bool AlphaPaletteSplit(CxImage *dest);
+//@}
+
+protected:
+/** \addtogroup Protected */ //@{
+ BYTE BlindAlphaGet(const long x,const long y);
+//@}
+#endif //CXIMAGE_SUPPORT_ALPHA
+
+public:
+#if CXIMAGE_SUPPORT_LAYERS
+/** \addtogroup Layers */ //@{
+ bool LayerCreate(long position = -1);
+ bool LayerDelete(long position = -1);
+ void LayerDeleteAll();
+ CxImage* GetLayer(long position);
+ CxImage* GetParent() const;
+ long GetNumLayers() const;
+ long LayerDrawAll(HDC hdc, long x=0, long y=0, long cx = -1, long cy = -1, RECT* pClipRect = 0, bool bSmooth = false);
+ long LayerDrawAll(HDC hdc, const RECT& rect, RECT* pClipRect=NULL, bool bSmooth = false);
+//@}
+#endif //CXIMAGE_SUPPORT_LAYERS
+
+protected:
+/** \addtogroup Protected */ //@{
+ void Startup(DWORD imagetype = 0);
+ void CopyInfo(const CxImage &src);
+ void Ghost(const CxImage *src);
+ void RGBtoBGR(BYTE *buffer, int length);
+ static float HueToRGB(float n1,float n2, float hue);
+ void Bitfield2RGB(BYTE *src, DWORD redmask, DWORD greenmask, DWORD bluemask, BYTE bpp);
+ static int CompareColors(const void *elem1, const void *elem2);
+ short ntohs(const short word);
+ long ntohl(const long dword);
+ void bihtoh(BITMAPINFOHEADER* bih);
+
+ void* pDib; //contains the header, the palette, the pixels
+ BITMAPINFOHEADER head; //standard header
+ CXIMAGEINFO info; //extended information
+ BYTE* pSelection; //selected region
+ BYTE* pAlpha; //alpha channel
+ CxImage** ppLayers; //generic layers
+ CxImage** ppFrames;
+//@}
+};
+
+////////////////////////////////////////////////////////////////////////////
+#endif // !defined(__CXIMAGE_H)
diff --git a/CxImage/ximainfo.cpp b/CxImage/ximainfo.cpp
new file mode 100644
index 00000000..73a1453a
--- /dev/null
+++ b/CxImage/ximainfo.cpp
@@ -0,0 +1,919 @@
+// ximainfo.cpp : main attributes
+/* 03/10/2004 v1.00 - Davide Pizzolato - www.xdp.it
+ * CxImage version 6.0.0 02/Feb/2008
+ */
+
+#include "ximage.h"
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return the color used for transparency, and/or for background color
+ */
+RGBQUAD CxImage::GetTransColor()
+{
+ if (head.biBitCount<24 && info.nBkgndIndex>=0) return GetPaletteColor((BYTE)info.nBkgndIndex);
+ return info.nBkgndColor;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the index used for transparency. Returns -1 for no transparancy.
+ */
+long CxImage::GetTransIndex() const
+{
+ return info.nBkgndIndex;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the index used for transparency with 1, 4 and 8 bpp images. Set to -1 to remove the effect.
+ */
+void CxImage::SetTransIndex(long idx)
+{
+ if (idx<(long)head.biClrUsed)
+ info.nBkgndIndex = idx;
+ else
+ info.nBkgndIndex = 0;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the color used for transparency with 24 bpp images.
+ * You must call SetTransIndex(0) to enable the effect, SetTransIndex(-1) to disable it.
+ */
+void CxImage::SetTransColor(RGBQUAD rgb)
+{
+ rgb.rgbReserved=0;
+ info.nBkgndColor = rgb;
+}
+////////////////////////////////////////////////////////////////////////////////
+bool CxImage::IsTransparent() const
+{
+ return info.nBkgndIndex>=0; //
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns true if the image has 256 colors or less.
+ */
+bool CxImage::IsIndexed() const
+{
+ return head.biClrUsed!=0;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return 1 = indexed, 2 = RGB, 4 = RGBA
+ */
+BYTE CxImage::GetColorType()
+{
+ BYTE b = (BYTE)((head.biBitCount>8) ? 2 /*COLORTYPE_COLOR*/ : 1 /*COLORTYPE_PALETTE*/);
+#if CXIMAGE_SUPPORT_ALPHA
+ if (AlphaIsValid()) b = 4 /*COLORTYPE_ALPHA*/;
+#endif //CXIMAGE_SUPPORT_ALPHA
+ return b;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return Resolution for TIFF, JPEG, PNG and BMP formats.
+ */
+long CxImage::GetXDPI() const
+{
+ return info.xDPI;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return Resolution for TIFF, JPEG, PNG and BMP formats.
+ */
+long CxImage::GetYDPI() const
+{
+ return info.yDPI;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Set resolution for TIFF, JPEG, PNG and BMP formats.
+ */
+void CxImage::SetXDPI(long dpi)
+{
+ if (dpi<=0) dpi = CXIMAGE_DEFAULT_DPI;
+ info.xDPI = dpi;
+ head.biXPelsPerMeter = (long) floor(dpi * 10000.0 / 254.0 + 0.5);
+ if (pDib) ((BITMAPINFOHEADER*)pDib)->biXPelsPerMeter = head.biXPelsPerMeter;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Set resolution for TIFF, JPEG, PNG and BMP formats.
+ */
+void CxImage::SetYDPI(long dpi)
+{
+ if (dpi<=0) dpi = CXIMAGE_DEFAULT_DPI;
+ info.yDPI = dpi;
+ head.biYPelsPerMeter = (long) floor(dpi * 10000.0 / 254.0 + 0.5);
+ if (pDib) ((BITMAPINFOHEADER*)pDib)->biYPelsPerMeter = head.biYPelsPerMeter;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \sa SetFlags
+ */
+DWORD CxImage::GetFlags() const
+{
+ return info.dwFlags;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Image flags, for future use
+ * \param flags
+ * - 0x??00000 = reserved for 16 bit, CMYK, multilayer
+ * - 0x00??0000 = blend modes
+ * - 0x0000???? = layer id or user flags
+ *
+ * \param bLockReservedFlags protects the "reserved" and "blend modes" flags
+ */
+void CxImage::SetFlags(DWORD flags, bool bLockReservedFlags)
+{
+ if (bLockReservedFlags) info.dwFlags = flags & 0x0000ffff;
+ else info.dwFlags = flags;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \sa SetCodecOption
+ */
+DWORD CxImage::GetCodecOption(DWORD imagetype)
+{
+ imagetype = GetTypeIndexFromId(imagetype);
+ if (imagetype==0){
+ imagetype = GetTypeIndexFromId(GetType());
+ }
+ return info.dwCodecOpt[imagetype];
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Encode option for GIF, TIF and JPG.
+ * - GIF : 0 = LZW (default), 1 = none, 2 = RLE.
+ * - TIF : 0 = automatic (default), or a valid compression code as defined in "tiff.h" (COMPRESSION_NONE = 1, COMPRESSION_CCITTRLE = 2, ...)
+ * - JPG : valid values stored in enum CODEC_OPTION ( ENCODE_BASELINE = 0x01, ENCODE_PROGRESSIVE = 0x10, ...)
+ * - RAW : valid values stored in enum CODEC_OPTION ( DECODE_QUALITY_LIN = 0x00, DECODE_QUALITY_VNG = 0x01, ...)
+ *
+ * \return true if everything is ok
+ */
+bool CxImage::SetCodecOption(DWORD opt, DWORD imagetype)
+{
+ imagetype = GetTypeIndexFromId(imagetype);
+ if (imagetype==0){
+ imagetype = GetTypeIndexFromId(GetType());
+ }
+ info.dwCodecOpt[imagetype] = opt;
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return internal hDib object..
+ */
+void* CxImage::GetDIB() const
+{
+ return pDib;
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::GetHeight() const
+{
+ return head.biHeight;
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::GetWidth() const
+{
+ return head.biWidth;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return DWORD aligned width of the image.
+ */
+DWORD CxImage::GetEffWidth() const
+{
+ return info.dwEffWidth;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return 2, 16, 256; 0 for RGB images.
+ */
+DWORD CxImage::GetNumColors() const
+{
+ return head.biClrUsed;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return: 1, 4, 8, 24.
+ */
+WORD CxImage::GetBpp() const
+{
+ return head.biBitCount;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return original image format
+ * \sa ENUM_CXIMAGE_FORMATS.
+ */
+DWORD CxImage::GetType() const
+{
+ return info.dwType;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * change image format identifier
+ * \sa ENUM_CXIMAGE_FORMATS.
+ */
+bool CxImage::SetType(DWORD type)
+{
+ switch (type){
+#if CXIMAGE_SUPPORT_BMP
+ case CXIMAGE_FORMAT_BMP:
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ case CXIMAGE_FORMAT_GIF:
+#endif
+#if CXIMAGE_SUPPORT_JPG
+ case CXIMAGE_FORMAT_JPG:
+#endif
+#if CXIMAGE_SUPPORT_PNG
+ case CXIMAGE_FORMAT_PNG:
+#endif
+#if CXIMAGE_SUPPORT_MNG
+ case CXIMAGE_FORMAT_MNG:
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ case CXIMAGE_FORMAT_ICO:
+#endif
+#if CXIMAGE_SUPPORT_TIF
+ case CXIMAGE_FORMAT_TIF:
+#endif
+#if CXIMAGE_SUPPORT_TGA
+ case CXIMAGE_FORMAT_TGA:
+#endif
+#if CXIMAGE_SUPPORT_PCX
+ case CXIMAGE_FORMAT_PCX:
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+ case CXIMAGE_FORMAT_WBMP:
+#endif
+#if CXIMAGE_SUPPORT_WMF
+ case CXIMAGE_FORMAT_WMF:
+#endif
+#if CXIMAGE_SUPPORT_JBG
+ case CXIMAGE_FORMAT_JBG:
+#endif
+#if CXIMAGE_SUPPORT_JP2
+ case CXIMAGE_FORMAT_JP2:
+#endif
+#if CXIMAGE_SUPPORT_JPC
+ case CXIMAGE_FORMAT_JPC:
+#endif
+#if CXIMAGE_SUPPORT_PGX
+ case CXIMAGE_FORMAT_PGX:
+#endif
+#if CXIMAGE_SUPPORT_PNM
+ case CXIMAGE_FORMAT_PNM:
+#endif
+#if CXIMAGE_SUPPORT_RAS
+ case CXIMAGE_FORMAT_RAS:
+#endif
+#if CXIMAGE_SUPPORT_SKA
+ case CXIMAGE_FORMAT_SKA:
+#endif
+#if CXIMAGE_SUPPORT_RAW
+ case CXIMAGE_FORMAT_RAW:
+#endif
+ info.dwType = type;
+ return true;
+ }
+ info.dwType = CXIMAGE_FORMAT_UNKNOWN;
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::GetNumTypes()
+{
+ return CMAX_IMAGE_FORMATS-1;
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::GetTypeIdFromName(const TCHAR* ext)
+{
+#if CXIMAGE_SUPPORT_BMP
+ if (_tcsnicmp(ext,_T("bmp"),3)==0 ) return CXIMAGE_FORMAT_BMP;
+#endif
+#if CXIMAGE_SUPPORT_JPG
+ if (_tcsnicmp(ext,_T("jpg"),3)==0 ||
+ _tcsnicmp(ext,_T("jpe"),3)==0 ||
+ _tcsnicmp(ext,_T("jfi"),3)==0 ) return CXIMAGE_FORMAT_JPG;
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ if (_tcsnicmp(ext,_T("gif"),3)==0 ) return CXIMAGE_FORMAT_GIF;
+#endif
+#if CXIMAGE_SUPPORT_PNG
+ if (_tcsnicmp(ext,_T("png"),3)==0 ) return CXIMAGE_FORMAT_PNG;
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ if (_tcsnicmp(ext,_T("ico"),3)==0 ||
+ _tcsnicmp(ext,_T("cur"),3)==0 ) return CXIMAGE_FORMAT_ICO;
+#endif
+#if CXIMAGE_SUPPORT_TIF
+ if (_tcsnicmp(ext,_T("tif"),3)==0 ) return CXIMAGE_FORMAT_TIF;
+#endif
+#if CXIMAGE_SUPPORT_TGA
+ if (_tcsnicmp(ext,_T("tga"),3)==0 ) return CXIMAGE_FORMAT_TGA;
+#endif
+#if CXIMAGE_SUPPORT_PCX
+ if (_tcsnicmp(ext,_T("pcx"),3)==0 ) return CXIMAGE_FORMAT_PCX;
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+ if (_tcsnicmp(ext,_T("wbm"),3)==0 ) return CXIMAGE_FORMAT_WBMP;
+#endif
+#if CXIMAGE_SUPPORT_WMF
+ if (_tcsnicmp(ext,_T("wmf"),3)==0 ||
+ _tcsnicmp(ext,_T("emf"),3)==0 ) return CXIMAGE_FORMAT_WMF;
+#endif
+#if CXIMAGE_SUPPORT_JP2
+ if (_tcsnicmp(ext,_T("jp2"),3)==0 ||
+ _tcsnicmp(ext,_T("j2k"),3)==0 ) return CXIMAGE_FORMAT_JP2;
+#endif
+#if CXIMAGE_SUPPORT_JPC
+ if (_tcsnicmp(ext,_T("jpc"),3)==0 ||
+ _tcsnicmp(ext,_T("j2c"),3)==0 ) return CXIMAGE_FORMAT_JPC;
+#endif
+#if CXIMAGE_SUPPORT_PGX
+ if (_tcsnicmp(ext,_T("pgx"),3)==0 ) return CXIMAGE_FORMAT_PGX;
+#endif
+#if CXIMAGE_SUPPORT_RAS
+ if (_tcsnicmp(ext,_T("ras"),3)==0 ) return CXIMAGE_FORMAT_RAS;
+#endif
+#if CXIMAGE_SUPPORT_PNM
+ if (_tcsnicmp(ext,_T("pnm"),3)==0 ||
+ _tcsnicmp(ext,_T("pgm"),3)==0 ||
+ _tcsnicmp(ext,_T("ppm"),3)==0 ) return CXIMAGE_FORMAT_PNM;
+#endif
+#if CXIMAGE_SUPPORT_JBG
+ if (_tcsnicmp(ext,_T("jbg"),3)==0 ) return CXIMAGE_FORMAT_JBG;
+#endif
+#if CXIMAGE_SUPPORT_MNG
+ if (_tcsnicmp(ext,_T("mng"),3)==0 ||
+ _tcsnicmp(ext,_T("jng"),3)==0 ) return CXIMAGE_FORMAT_MNG;
+#endif
+#if CXIMAGE_SUPPORT_SKA
+ if (_tcsnicmp(ext,_T("ska"),3)==0 ) return CXIMAGE_FORMAT_SKA;
+#endif
+#if CXIMAGE_SUPPORT_RAW
+ if (_tcsnicmp(ext,_T("nef"),3)==0 ||
+ _tcsnicmp(ext,_T("crw"),3)==0 ||
+ _tcsnicmp(ext,_T("cr2"),3)==0 ||
+ _tcsnicmp(ext,_T("dng"),3)==0 ||
+ _tcsnicmp(ext,_T("arw"),3)==0 ||
+ _tcsnicmp(ext,_T("erf"),3)==0 ||
+ _tcsnicmp(ext,_T("3fr"),3)==0 ||
+ _tcsnicmp(ext,_T("dcr"),3)==0 ||
+ _tcsnicmp(ext,_T("raw"),3)==0 ||
+ _tcsnicmp(ext,_T("x3f"),3)==0 ||
+ _tcsnicmp(ext,_T("mef"),3)==0 ||
+ _tcsnicmp(ext,_T("raf"),3)==0 ||
+ _tcsnicmp(ext,_T("mrw"),3)==0 ||
+ _tcsnicmp(ext,_T("pef"),3)==0 ||
+ _tcsnicmp(ext,_T("sr2"),3)==0 ||
+ _tcsnicmp(ext,_T("orf"),3)==0 ) return CXIMAGE_FORMAT_RAW;
+#endif
+
+ return CXIMAGE_FORMAT_UNKNOWN;
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::GetTypeIdFromIndex(const DWORD index)
+{
+ DWORD n;
+
+ n=0; if (index == n) return CXIMAGE_FORMAT_UNKNOWN;
+#if CXIMAGE_SUPPORT_BMP
+ n++; if (index == n) return CXIMAGE_FORMAT_BMP;
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ n++; if (index == n) return CXIMAGE_FORMAT_GIF;
+#endif
+#if CXIMAGE_SUPPORT_JPG
+ n++; if (index == n) return CXIMAGE_FORMAT_JPG;
+#endif
+#if CXIMAGE_SUPPORT_PNG
+ n++; if (index == n) return CXIMAGE_FORMAT_PNG;
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ n++; if (index == n) return CXIMAGE_FORMAT_ICO;
+#endif
+#if CXIMAGE_SUPPORT_TIF
+ n++; if (index == n) return CXIMAGE_FORMAT_TIF;
+#endif
+#if CXIMAGE_SUPPORT_TGA
+ n++; if (index == n) return CXIMAGE_FORMAT_TGA;
+#endif
+#if CXIMAGE_SUPPORT_PCX
+ n++; if (index == n) return CXIMAGE_FORMAT_PCX;
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+ n++; if (index == n) return CXIMAGE_FORMAT_WBMP;
+#endif
+#if CXIMAGE_SUPPORT_WMF
+ n++; if (index == n) return CXIMAGE_FORMAT_WMF;
+#endif
+#if CXIMAGE_SUPPORT_JP2
+ n++; if (index == n) return CXIMAGE_FORMAT_JP2;
+#endif
+#if CXIMAGE_SUPPORT_JPC
+ n++; if (index == n) return CXIMAGE_FORMAT_JPC;
+#endif
+#if CXIMAGE_SUPPORT_PGX
+ n++; if (index == n) return CXIMAGE_FORMAT_PGX;
+#endif
+#if CXIMAGE_SUPPORT_PNM
+ n++; if (index == n) return CXIMAGE_FORMAT_PNM;
+#endif
+#if CXIMAGE_SUPPORT_RAS
+ n++; if (index == n) return CXIMAGE_FORMAT_RAS;
+#endif
+#if CXIMAGE_SUPPORT_JBG
+ n++; if (index == n) return CXIMAGE_FORMAT_JBG;
+#endif
+#if CXIMAGE_SUPPORT_MNG
+ n++; if (index == n) return CXIMAGE_FORMAT_MNG;
+#endif
+#if CXIMAGE_SUPPORT_SKA
+ n++; if (index == n) return CXIMAGE_FORMAT_SKA;
+#endif
+#if CXIMAGE_SUPPORT_RAW
+ n++; if (index == n) return CXIMAGE_FORMAT_RAW;
+#endif
+
+ return CXIMAGE_FORMAT_UNKNOWN;
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::GetTypeIndexFromId(const DWORD id)
+{
+ DWORD n;
+
+ n=0; if (id == CXIMAGE_FORMAT_UNKNOWN) return n;
+#if CXIMAGE_SUPPORT_BMP
+ n++; if (id == CXIMAGE_FORMAT_BMP) return n;
+#endif
+#if CXIMAGE_SUPPORT_GIF
+ n++; if (id == CXIMAGE_FORMAT_GIF) return n;
+#endif
+#if CXIMAGE_SUPPORT_JPG
+ n++; if (id == CXIMAGE_FORMAT_JPG) return n;
+#endif
+#if CXIMAGE_SUPPORT_PNG
+ n++; if (id == CXIMAGE_FORMAT_PNG) return n;
+#endif
+#if CXIMAGE_SUPPORT_ICO
+ n++; if (id == CXIMAGE_FORMAT_ICO) return n;
+#endif
+#if CXIMAGE_SUPPORT_TIF
+ n++; if (id == CXIMAGE_FORMAT_TIF) return n;
+#endif
+#if CXIMAGE_SUPPORT_TGA
+ n++; if (id == CXIMAGE_FORMAT_TGA) return n;
+#endif
+#if CXIMAGE_SUPPORT_PCX
+ n++; if (id == CXIMAGE_FORMAT_PCX) return n;
+#endif
+#if CXIMAGE_SUPPORT_WBMP
+ n++; if (id == CXIMAGE_FORMAT_WBMP) return n;
+#endif
+#if CXIMAGE_SUPPORT_WMF
+ n++; if (id == CXIMAGE_FORMAT_WMF) return n;
+#endif
+#if CXIMAGE_SUPPORT_JP2
+ n++; if (id == CXIMAGE_FORMAT_JP2) return n;
+#endif
+#if CXIMAGE_SUPPORT_JPC
+ n++; if (id == CXIMAGE_FORMAT_JPC) return n;
+#endif
+#if CXIMAGE_SUPPORT_PGX
+ n++; if (id == CXIMAGE_FORMAT_PGX) return n;
+#endif
+#if CXIMAGE_SUPPORT_PNM
+ n++; if (id == CXIMAGE_FORMAT_PNM) return n;
+#endif
+#if CXIMAGE_SUPPORT_RAS
+ n++; if (id == CXIMAGE_FORMAT_RAS) return n;
+#endif
+#if CXIMAGE_SUPPORT_JBG
+ n++; if (id == CXIMAGE_FORMAT_JBG) return n;
+#endif
+#if CXIMAGE_SUPPORT_MNG
+ n++; if (id == CXIMAGE_FORMAT_MNG) return n;
+#endif
+#if CXIMAGE_SUPPORT_SKA
+ n++; if (id == CXIMAGE_FORMAT_SKA) return n;
+#endif
+#if CXIMAGE_SUPPORT_RAW
+ n++; if (id == CXIMAGE_FORMAT_RAW) return n;
+#endif
+
+ return 0;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return current frame delay in milliseconds. Only for GIF and MNG formats.
+ */
+DWORD CxImage::GetFrameDelay() const
+{
+ return info.dwFrameDelay;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets current frame delay. Only for GIF format.
+ * \param d = delay in milliseconds
+ */
+void CxImage::SetFrameDelay(DWORD d)
+{
+ info.dwFrameDelay=d;
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::GetOffset(long *x,long *y)
+{
+ *x=info.xOffset;
+ *y=info.yOffset;
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::SetOffset(long x,long y)
+{
+ info.xOffset=x;
+ info.yOffset=y;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \sa SetJpegQuality, GetJpegQualityF
+ * \author [DP]; changes [Stefan Schürmans]
+ */
+BYTE CxImage::GetJpegQuality() const
+{
+ return (BYTE)(info.fQuality + 0.5f);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \sa SetJpegQuality, GetJpegQuality
+ * \author [Stefan Schürmans]
+ */
+float CxImage::GetJpegQualityF() const
+{
+ return info.fQuality;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * quality level for JPEG and JPEG2000
+ * \param q: can be from 0 to 100
+ * \author [DP]; changes [Stefan Schürmans]
+ */
+void CxImage::SetJpegQuality(BYTE q){
+ info.fQuality = (float)q;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * quality level for JPEG and JPEG2000
+ * necessary for JPEG2000 when quality is between 0.0 and 1.0
+ * \param q: can be from 0.0 to 100.0
+ * \author [Stefan Schürmans]
+ */
+void CxImage::SetJpegQualityF(float q){
+ if (q>0) info.fQuality = q;
+ else info.fQuality = 0.0f;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \sa SetJpegScale
+ */
+BYTE CxImage::GetJpegScale() const
+{
+ return info.nJpegScale;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * scaling down during JPEG decoding valid numbers are 1, 2, 4, 8
+ * \author [ignacio]
+ */
+void CxImage::SetJpegScale(BYTE q){
+ info.nJpegScale = q;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Used to monitor the slow loops.
+ * \return value is from 0 to 100.
+ * \sa SetProgress
+ */
+long CxImage::GetProgress() const
+{
+ return info.nProgress;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return the escape code.
+ * \sa SetEscape
+ */
+long CxImage::GetEscape() const
+{
+ return info.nEscape;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Forces the value of the internal progress variable.
+ * \param p should be from 0 to 100.
+ * \sa GetProgress
+ */
+void CxImage::SetProgress(long p)
+{
+ info.nProgress = p;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Used to quit the slow loops or the codecs.
+ * - SetEscape(-1) before Decode forces the function to exit, right after
+ * the image width and height are available ( for bmp, jpg, gif, tif )
+ */
+void CxImage::SetEscape(long i)
+{
+ info.nEscape = i;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks if the image is correctly initializated.
+ */
+bool CxImage::IsValid() const
+{
+ return pDib!=0;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * True if the image is enabled for painting.
+ */
+bool CxImage::IsEnabled() const
+{
+ return info.bEnabled;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Enables/disables the image.
+ */
+void CxImage::Enable(bool enable)
+{
+ info.bEnabled=enable;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * This function must be used after a Decode() / Load() call.
+ * Use the sequence SetFrame(-1); Load(...); GetNumFrames();
+ * to get the number of images without loading the first image.
+ * \return the number of images in the file.
+ */
+long CxImage::GetNumFrames() const
+{
+ return info.nNumFrames;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return the current selected image (zero-based index).
+ */
+long CxImage::GetFrame() const
+{
+ return info.nFrame;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the image number that the next Decode() / Load() call will load
+ */
+void CxImage::SetFrame(long nFrame){
+ info.nFrame=nFrame;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the method for drawing the frame related to others
+ * \sa GetDisposalMethod
+ */
+void CxImage::SetDisposalMethod(BYTE dm)
+{ info.dispmeth=dm; }
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the method for drawing the frame related to others
+ * Values : 0 - No disposal specified. The decoder is
+ * not required to take any action.
+ * 1 - Do not dispose. The graphic is to be left
+ * in place.
+ * 2 - Restore to background color. The area used by the
+ * graphic must be restored to the background color.
+ * 3 - Restore to previous. The decoder is required to
+ * restore the area overwritten by the graphic with
+ * what was there prior to rendering the graphic.
+ * 4-7 - To be defined.
+ */
+BYTE CxImage::GetDisposalMethod() const
+{ return info.dispmeth; }
+////////////////////////////////////////////////////////////////////////////////
+bool CxImage::GetRetreiveAllFrames() const
+{ return info.bGetAllFrames; }
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::SetRetreiveAllFrames(bool flag)
+{ info.bGetAllFrames = flag; }
+////////////////////////////////////////////////////////////////////////////////
+CxImage * CxImage::GetFrame(long nFrame) const
+{
+ if ( ppFrames == NULL) return NULL;
+ if ( info.nNumFrames == 0) return NULL;
+ if ( nFrame >= info.nNumFrames ) return NULL;
+ if ( nFrame < 0) nFrame = info.nNumFrames - 1;
+ return ppFrames[nFrame];
+}
+////////////////////////////////////////////////////////////////////////////////
+short CxImage::ntohs(const short word)
+{
+ if (info.bLittleEndianHost) return word;
+ return ( (word & 0xff) << 8 ) | ( (word >> 8) & 0xff );
+}
+////////////////////////////////////////////////////////////////////////////////
+long CxImage::ntohl(const long dword)
+{
+ if (info.bLittleEndianHost) return dword;
+ return ((dword & 0xff) << 24 ) | ((dword & 0xff00) << 8 ) |
+ ((dword >> 8) & 0xff00) | ((dword >> 24) & 0xff);
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::bihtoh(BITMAPINFOHEADER* bih)
+{
+ bih->biSize = ntohl(bih->biSize);
+ bih->biWidth = ntohl(bih->biWidth);
+ bih->biHeight = ntohl(bih->biHeight);
+ bih->biPlanes = ntohs(bih->biPlanes);
+ bih->biBitCount = ntohs(bih->biBitCount);
+ bih->biCompression = ntohl(bih->biCompression);
+ bih->biSizeImage = ntohl(bih->biSizeImage);
+ bih->biXPelsPerMeter = ntohl(bih->biXPelsPerMeter);
+ bih->biYPelsPerMeter = ntohl(bih->biYPelsPerMeter);
+ bih->biClrUsed = ntohl(bih->biClrUsed);
+ bih->biClrImportant = ntohl(bih->biClrImportant);
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the last reported error.
+ */
+const char* CxImage::GetLastError()
+{
+ return info.szLastError;
+}
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_LAYERS
+DWORD CxImage::DumpSize()
+{
+ DWORD n;
+ n = sizeof(BITMAPINFOHEADER) + sizeof(CXIMAGEINFO) + GetSize();
+
+ if (pAlpha){
+ n += 1 + head.biWidth * head.biHeight;
+ } else n++;
+
+ if (pSelection){
+ n += 1 + head.biWidth * head.biHeight;
+ } else n++;
+
+ if (ppLayers){
+ for (long m=0; mDumpSize();
+ }
+ }
+ } else n++;
+
+ if (ppFrames){
+ for (long m=0; mDumpSize();
+ }
+ }
+ } else n++;
+
+ return n;
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::Dump(BYTE * dst)
+{
+ if (!dst) return 0;
+
+ memcpy(dst,&head,sizeof(BITMAPINFOHEADER));
+ dst += sizeof(BITMAPINFOHEADER);
+
+ memcpy(dst,&info,sizeof(CXIMAGEINFO));
+ dst += sizeof(CXIMAGEINFO);
+
+ memcpy(dst,pDib,GetSize());
+ dst += GetSize();
+
+ if (pAlpha){
+ memset(dst++, 1, 1);
+ memcpy(dst,pAlpha,head.biWidth * head.biHeight);
+ dst += head.biWidth * head.biHeight;
+ } else {
+ memset(dst++, 0, 1);
+ }
+
+ if (pSelection){
+ memset(dst++, 1, 1);
+ memcpy(dst,pSelection,head.biWidth * head.biHeight);
+ dst += head.biWidth * head.biHeight;
+ } else {
+ memset(dst++, 0, 1);
+ }
+
+ if (ppLayers){
+ memset(dst++, 1, 1);
+ for (long m=0; mDump(dst);
+ }
+ }
+ } else {
+ memset(dst++, 0, 1);
+ }
+
+ if (ppFrames){
+ memset(dst++, 1, 1);
+ for (long m=0; mDump(dst);
+ }
+ }
+ } else {
+ memset(dst++, 0, 1);
+ }
+
+ return DumpSize();
+}
+////////////////////////////////////////////////////////////////////////////////
+DWORD CxImage::UnDump(const BYTE * src)
+{
+ if (!src)
+ return 0;
+ if (!Destroy())
+ return 0;
+ if (!DestroyFrames())
+ return 0;
+
+ DWORD n = 0;
+
+ memcpy(&head,src,sizeof(BITMAPINFOHEADER));
+ n += sizeof(BITMAPINFOHEADER);
+
+ memcpy(&info,&src[n],sizeof(CXIMAGEINFO));
+ n += sizeof(CXIMAGEINFO);
+
+ if (!Create(head.biWidth, head.biHeight, head.biBitCount, info.dwType))
+ return 0;
+
+ memcpy(pDib,&src[n],GetSize());
+ n += GetSize();
+
+ if (src[n++]){
+ if (AlphaCreate()){
+ memcpy(pAlpha, &src[n], head.biWidth * head.biHeight);
+ }
+ n += head.biWidth * head.biHeight;
+ }
+
+ if (src[n++]){
+ RECT box = info.rSelectionBox;
+ if (SelectionCreate()){
+ info.rSelectionBox = box;
+ memcpy(pSelection, &src[n], head.biWidth * head.biHeight);
+ }
+ n += head.biWidth * head.biHeight;
+ }
+
+ if (src[n++]){
+ ppLayers = new CxImage*[info.nNumLayers];
+ for (long m=0; mUnDump(&src[n]);
+ }
+ }
+
+ if (src[n++]){
+ ppFrames = new CxImage*[info.nNumFrames];
+ for (long m=0; mUnDump(&src[n]);
+ }
+ }
+
+ return n;
+}
+#endif
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * \return A.BBCCCDDDD
+ * - A = main version
+ * - BB = main revision
+ * - CCC = minor revision (letter)
+ * - DDDD = experimental revision
+ */
+const float CxImage::GetVersionNumber()
+{
+ return 6.000000015f;
+}
+////////////////////////////////////////////////////////////////////////////////
+const TCHAR* CxImage::GetVersion()
+{
+ static const TCHAR CxImageVersion[] = _T("CxImage 6.0.0");
+ return (CxImageVersion);
+}
+////////////////////////////////////////////////////////////////////////////////
diff --git a/CxImage/ximaint.cpp b/CxImage/ximaint.cpp
new file mode 100644
index 00000000..989d76cf
--- /dev/null
+++ b/CxImage/ximaint.cpp
@@ -0,0 +1,1056 @@
+// xImaInt.cpp : interpolation functions
+/* 02/2004 - Branko Brevensek
+ * CxImage version 6.0.0 02/Feb/2008 - Davide Pizzolato - www.xdp.it
+ */
+
+#include "ximage.h"
+#include "ximath.h"
+
+#if CXIMAGE_SUPPORT_INTERPOLATION
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recalculates coordinates according to specified overflow method.
+ * If pixel (x,y) lies within image, nothing changes.
+ *
+ * \param x, y - coordinates of pixel
+ * \param ofMethod - overflow method
+ *
+ * \return x, y - new coordinates (pixel (x,y) now lies inside image)
+ *
+ * \author ***bd*** 2.2004
+ */
+void CxImage::OverflowCoordinates(long &x, long &y, OverflowMethod const ofMethod)
+{
+ if (IsInside(x,y)) return; //if pixel is within bounds, no change
+ switch (ofMethod) {
+ case OM_REPEAT:
+ //clip coordinates
+ x=max(x,0); x=min(x, head.biWidth-1);
+ y=max(y,0); y=min(y, head.biHeight-1);
+ break;
+ case OM_WRAP:
+ //wrap coordinates
+ x = x % head.biWidth;
+ y = y % head.biHeight;
+ if (x<0) x = head.biWidth + x;
+ if (y<0) y = head.biHeight + y;
+ break;
+ case OM_MIRROR:
+ //mirror pixels near border
+ if (x<0) x=((-x) % head.biWidth);
+ else if (x>=head.biWidth) x=head.biWidth-(x % head.biWidth + 1);
+ if (y<0) y=((-y) % head.biHeight);
+ else if (y>=head.biHeight) y=head.biHeight-(y % head.biHeight + 1);
+ break;
+ default:
+ return;
+ }//switch
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * See OverflowCoordinates for integer version
+ * \author ***bd*** 2.2004
+ */
+void CxImage::OverflowCoordinates(float &x, float &y, OverflowMethod const ofMethod)
+{
+ if (x>=0 && x=0 && y=head.biWidth) x=head.biWidth-((float)fmod(x, (float) head.biWidth) + 1);
+ if (y<0) y=(float)fmod(-y, (float) head.biHeight);
+ else if (y>=head.biHeight) y=head.biHeight-((float)fmod(y, (float) head.biHeight) + 1);
+ break;
+ default:
+ return;
+ }//switch
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Method return pixel color. Different methods are implemented for out of bounds pixels.
+ * If an image has alpha channel, alpha value is returned in .RGBReserved.
+ *
+ * \param x,y : pixel coordinates
+ * \param ofMethod : out-of-bounds method:
+ * - OF_WRAP - wrap over to pixels on other side of the image
+ * - OF_REPEAT - repeat last pixel on the edge
+ * - OF_COLOR - return input value of color
+ * - OF_BACKGROUND - return background color (if not set, return input color)
+ * - OF_TRANSPARENT - return transparent pixel
+ *
+ * \param rplColor : input color (returned for out-of-bound coordinates in OF_COLOR mode and if other mode is not applicable)
+ *
+ * \return color : color of pixel
+ * \author ***bd*** 2.2004
+ */
+RGBQUAD CxImage::GetPixelColorWithOverflow(long x, long y, OverflowMethod const ofMethod, RGBQUAD* const rplColor)
+{
+ RGBQUAD color; //color to return
+ if ((!IsInside(x,y)) || pDib==NULL) { //is pixel within bouns?:
+ //pixel is out of bounds or no DIB
+ if (rplColor!=NULL)
+ color=*rplColor;
+ else {
+ color.rgbRed=color.rgbGreen=color.rgbBlue=255; color.rgbReserved=0; //default replacement colour: white transparent
+ }//if
+ if (pDib==NULL) return color;
+ //pixel is out of bounds:
+ switch (ofMethod) {
+ case OM_TRANSPARENT:
+#if CXIMAGE_SUPPORT_ALPHA
+ if (AlphaIsValid()) {
+ //alpha transparency is supported and image has alpha layer
+ color.rgbReserved=0;
+ } else {
+#endif //CXIMAGE_SUPPORT_ALPHA
+ //no alpha transparency
+ if (GetTransIndex()>=0) {
+ color=GetTransColor(); //single color transparency enabled (return transparent color)
+ }//if
+#if CXIMAGE_SUPPORT_ALPHA
+ }//if
+#endif //CXIMAGE_SUPPORT_ALPHA
+ return color;
+ case OM_BACKGROUND:
+ //return background color (if it exists, otherwise input value)
+ if (info.nBkgndIndex >= 0) {
+ if (head.biBitCount<24) color = GetPaletteColor((BYTE)info.nBkgndIndex);
+ else color = info.nBkgndColor;
+ }//if
+ return color;
+ case OM_REPEAT:
+ case OM_WRAP:
+ case OM_MIRROR:
+ OverflowCoordinates(x,y,ofMethod);
+ break;
+ default:
+ //simply return replacement color (OM_COLOR and others)
+ return color;
+ }//switch
+ }//if
+ //just return specified pixel (it's within bounds)
+ return BlindGetPixelColor(x,y);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * This method reconstructs image according to chosen interpolation method and then returns pixel (x,y).
+ * (x,y) can lie between actual image pixels. If (x,y) lies outside of image, method returns value
+ * according to overflow method.
+ * This method is very useful for geometrical image transformations, where destination pixel
+ * can often assume color value lying between source pixels.
+ *
+ * \param (x,y) - coordinates of pixel to return
+ * GPCI method recreates "analogue" image back from digital data, so x and y
+ * are float values and color value of point (1.1,1) will generally not be same
+ * as (1,1). Center of first pixel is at (0,0) and center of pixel right to it is (1,0).
+ * (0.5,0) is half way between these two pixels.
+ * \param inMethod - interpolation (reconstruction) method (kernel) to use:
+ * - IM_NEAREST_NEIGHBOUR - returns colour of nearest lying pixel (causes stairy look of
+ * processed images)
+ * - IM_BILINEAR - interpolates colour from four neighbouring pixels (softens image a bit)
+ * - IM_BICUBIC - interpolates from 16 neighbouring pixels (can produce "halo" artifacts)
+ * - IM_BICUBIC2 - interpolates from 16 neighbouring pixels (perhaps a bit less halo artifacts
+ than IM_BICUBIC)
+ * - IM_BSPLINE - interpolates from 16 neighbouring pixels (softens image, washes colours)
+ * (As far as I know, image should be prefiltered for this method to give
+ * good results... some other time :) )
+ * This method uses bicubic interpolation kernel from CXImage 5.99a and older
+ * versions.
+ * - IM_LANCZOS - interpolates from 12*12 pixels (slow, ringing artifacts)
+ *
+ * \param ofMethod - overflow method (see comments at GetPixelColorWithOverflow)
+ * \param rplColor - pointer to color used for out of borders pixels in OM_COLOR mode
+ * (and other modes if colour can't calculated in a specified way)
+ *
+ * \return interpolated color value (including interpolated alpha value, if image has alpha layer)
+ *
+ * \author ***bd*** 2.2004
+ */
+RGBQUAD CxImage::GetPixelColorInterpolated(
+ float x,float y,
+ InterpolationMethod const inMethod,
+ OverflowMethod const ofMethod,
+ RGBQUAD* const rplColor)
+{
+ //calculate nearest pixel
+ int xi=(int)(x); if (x<0) xi--; //these replace (incredibly slow) floor (Visual c++ 2003, AMD Athlon)
+ int yi=(int)(y); if (y<0) yi--;
+ RGBQUAD color; //calculated colour
+
+ switch (inMethod) {
+ case IM_NEAREST_NEIGHBOUR:
+ return GetPixelColorWithOverflow((long)(x+0.5f), (long)(y+0.5f), ofMethod, rplColor);
+ default: {
+ //IM_BILINEAR: bilinear interpolation
+ if (xi<-1 || xi>=head.biWidth || yi<-1 || yi>=head.biHeight) { //all 4 points are outside bounds?:
+ switch (ofMethod) {
+ case OM_COLOR: case OM_TRANSPARENT: case OM_BACKGROUND:
+ //we don't need to interpolate anything with all points outside in this case
+ return GetPixelColorWithOverflow(-999, -999, ofMethod, rplColor);
+ default:
+ //recalculate coordinates and use faster method later on
+ OverflowCoordinates(x,y,ofMethod);
+ xi=(int)(x); if (x<0) xi--; //x and/or y have changed ... recalculate xi and yi
+ yi=(int)(y); if (y<0) yi--;
+ }//switch
+ }//if
+ //get four neighbouring pixels
+ if ((xi+1)=0 && (yi+1)=0 && head.biClrUsed==0) {
+ //all pixels are inside RGB24 image... optimize reading (and use fixed point arithmetic)
+ WORD wt1=(WORD)((x-xi)*256.0f), wt2=(WORD)((y-yi)*256.0f);
+ WORD wd=wt1*wt2>>8;
+ WORD wb=wt1-wd;
+ WORD wc=wt2-wd;
+ WORD wa=256-wt1-wc;
+ WORD wrr,wgg,wbb;
+ BYTE *pxptr=(BYTE*)info.pImage+yi*info.dwEffWidth+xi*3;
+ wbb=wa*(*pxptr++); wgg=wa*(*pxptr++); wrr=wa*(*pxptr++);
+ wbb+=wb*(*pxptr++); wgg+=wb*(*pxptr++); wrr+=wb*(*pxptr);
+ pxptr+=(info.dwEffWidth-5); //move to next row
+ wbb+=wc*(*pxptr++); wgg+=wc*(*pxptr++); wrr+=wc*(*pxptr++);
+ wbb+=wd*(*pxptr++); wgg+=wd*(*pxptr++); wrr+=wd*(*pxptr);
+ color.rgbRed=(BYTE) (wrr>>8); color.rgbGreen=(BYTE) (wgg>>8); color.rgbBlue=(BYTE) (wbb>>8);
+#if CXIMAGE_SUPPORT_ALPHA
+ if (pAlpha) {
+ WORD waa;
+ //image has alpha layer... we have to do the same for alpha data
+ pxptr=AlphaGetPointer(xi,yi); //pointer to first byte
+ waa=wa*(*pxptr++); waa+=wb*(*pxptr); //first two pixels
+ pxptr+=(head.biWidth-1); //move to next row
+ waa+=wc*(*pxptr++); waa+=wd*(*pxptr); //and second row pixels
+ color.rgbReserved=(BYTE) (waa>>8);
+ } else
+#endif
+ { //Alpha not supported or no alpha at all
+ color.rgbReserved = 0;
+ }
+ return color;
+ } else {
+ //default (slower) way to get pixels (not RGB24 or some pixels out of borders)
+ float t1=x-xi, t2=y-yi;
+ float d=t1*t2;
+ float b=t1-d;
+ float c=t2-d;
+ float a=1-t1-c;
+ RGBQUAD rgb11,rgb21,rgb12,rgb22;
+ rgb11=GetPixelColorWithOverflow(xi, yi, ofMethod, rplColor);
+ rgb21=GetPixelColorWithOverflow(xi+1, yi, ofMethod, rplColor);
+ rgb12=GetPixelColorWithOverflow(xi, yi+1, ofMethod, rplColor);
+ rgb22=GetPixelColorWithOverflow(xi+1, yi+1, ofMethod, rplColor);
+ //calculate linear interpolation
+ color.rgbRed=(BYTE) (a*rgb11.rgbRed+b*rgb21.rgbRed+c*rgb12.rgbRed+d*rgb22.rgbRed);
+ color.rgbGreen=(BYTE) (a*rgb11.rgbGreen+b*rgb21.rgbGreen+c*rgb12.rgbGreen+d*rgb22.rgbGreen);
+ color.rgbBlue=(BYTE) (a*rgb11.rgbBlue+b*rgb21.rgbBlue+c*rgb12.rgbBlue+d*rgb22.rgbBlue);
+#if CXIMAGE_SUPPORT_ALPHA
+ if (AlphaIsValid())
+ color.rgbReserved=(BYTE) (a*rgb11.rgbReserved+b*rgb21.rgbReserved+c*rgb12.rgbReserved+d*rgb22.rgbReserved);
+ else
+#endif
+ { //Alpha not supported or no alpha at all
+ color.rgbReserved = 0;
+ }
+ return color;
+ }//if
+ }//default
+ case IM_BICUBIC:
+ case IM_BICUBIC2:
+ case IM_BSPLINE:
+ case IM_BOX:
+ case IM_HERMITE:
+ case IM_HAMMING:
+ case IM_SINC:
+ case IM_BLACKMAN:
+ case IM_BESSEL:
+ case IM_GAUSSIAN:
+ case IM_QUADRATIC:
+ case IM_MITCHELL:
+ case IM_CATROM:
+ case IM_HANNING:
+ case IM_POWER:
+ //bicubic interpolation(s)
+ if (((xi+2)<0) || ((xi-1)>=head.biWidth) || ((yi+2)<0) || ((yi-1)>=head.biHeight)) { //all points are outside bounds?:
+ switch (ofMethod) {
+ case OM_COLOR: case OM_TRANSPARENT: case OM_BACKGROUND:
+ //we don't need to interpolate anything with all points outside in this case
+ return GetPixelColorWithOverflow(-999, -999, ofMethod, rplColor);
+ break;
+ default:
+ //recalculate coordinates and use faster method later on
+ OverflowCoordinates(x,y,ofMethod);
+ xi=(int)(x); if (x<0) xi--; //x and/or y have changed ... recalculate xi and yi
+ yi=(int)(y); if (y<0) yi--;
+ }//switch
+ }//if
+
+ //some variables needed from here on
+ int xii,yii; //x any y integer indexes for loops
+ float kernel, kernelyc; //kernel cache
+ float kernelx[12], kernely[4]; //precalculated kernel values
+ float rr,gg,bb,aa; //accumulated color values
+ //calculate multiplication factors for all pixels
+ int i;
+ switch (inMethod) {
+ case IM_BICUBIC:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelCubic((float)(xi+i-1-x));
+ kernely[i]=KernelCubic((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_BICUBIC2:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelGeneralizedCubic((float)(xi+i-1-x), -0.5);
+ kernely[i]=KernelGeneralizedCubic((float)(yi+i-1-y), -0.5);
+ }//for i
+ break;
+ case IM_BSPLINE:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelBSpline((float)(xi+i-1-x));
+ kernely[i]=KernelBSpline((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_BOX:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelBox((float)(xi+i-1-x));
+ kernely[i]=KernelBox((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_HERMITE:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelHermite((float)(xi+i-1-x));
+ kernely[i]=KernelHermite((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_HAMMING:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelHamming((float)(xi+i-1-x));
+ kernely[i]=KernelHamming((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_SINC:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelSinc((float)(xi+i-1-x));
+ kernely[i]=KernelSinc((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_BLACKMAN:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelBlackman((float)(xi+i-1-x));
+ kernely[i]=KernelBlackman((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_BESSEL:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelBessel((float)(xi+i-1-x));
+ kernely[i]=KernelBessel((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_GAUSSIAN:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelGaussian((float)(xi+i-1-x));
+ kernely[i]=KernelGaussian((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_QUADRATIC:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelQuadratic((float)(xi+i-1-x));
+ kernely[i]=KernelQuadratic((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_MITCHELL:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelMitchell((float)(xi+i-1-x));
+ kernely[i]=KernelMitchell((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_CATROM:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelCatrom((float)(xi+i-1-x));
+ kernely[i]=KernelCatrom((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_HANNING:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelHanning((float)(xi+i-1-x));
+ kernely[i]=KernelHanning((float)(yi+i-1-y));
+ }//for i
+ break;
+ case IM_POWER:
+ for (i=0; i<4; i++) {
+ kernelx[i]=KernelPower((float)(xi+i-1-x));
+ kernely[i]=KernelPower((float)(yi+i-1-y));
+ }//for i
+ break;
+ }//switch
+ rr=gg=bb=aa=0;
+ if (((xi+2)=1 && ((yi+2)=1) && !IsIndexed()) {
+ //optimized interpolation (faster pixel reads) for RGB24 images with all pixels inside bounds
+ BYTE *pxptr, *pxptra;
+ for (yii=yi-1; yii255) rr=255; if (rr<0) rr=0; color.rgbRed=(BYTE) rr;
+ if (gg>255) gg=255; if (gg<0) gg=0; color.rgbGreen=(BYTE) gg;
+ if (bb>255) bb=255; if (bb<0) bb=0; color.rgbBlue=(BYTE) bb;
+#if CXIMAGE_SUPPORT_ALPHA
+ if (AlphaIsValid()) {
+ if (aa>255) aa=255; if (aa<0) aa=0; color.rgbReserved=(BYTE) aa;
+ } else
+#endif
+ { //Alpha not supported or no alpha at all
+ color.rgbReserved = 0;
+ }
+ return color;
+ case IM_LANCZOS:
+ //lanczos window (16*16) sinc interpolation
+ if (((xi+6)<0) || ((xi-5)>=head.biWidth) || ((yi+6)<0) || ((yi-5)>=head.biHeight)) {
+ //all points are outside bounds
+ switch (ofMethod) {
+ case OM_COLOR: case OM_TRANSPARENT: case OM_BACKGROUND:
+ //we don't need to interpolate anything with all points outside in this case
+ return GetPixelColorWithOverflow(-999, -999, ofMethod, rplColor);
+ break;
+ default:
+ //recalculate coordinates and use faster method later on
+ OverflowCoordinates(x,y,ofMethod);
+ xi=(int)(x); if (x<0) xi--; //x and/or y have changed ... recalculate xi and yi
+ yi=(int)(y); if (y<0) yi--;
+ }//switch
+ }//if
+
+ for (xii=xi-5; xii=0) && ((yi+6)=0) && !IsIndexed()) {
+ //optimized interpolation (faster pixel reads) for RGB24 images with all pixels inside bounds
+ BYTE *pxptr, *pxptra;
+ for (yii=yi-5; yii255) rr=255; if (rr<0) rr=0; color.rgbRed=(BYTE) rr;
+ if (gg>255) gg=255; if (gg<0) gg=0; color.rgbGreen=(BYTE) gg;
+ if (bb>255) bb=255; if (bb<0) bb=0; color.rgbBlue=(BYTE) bb;
+#if CXIMAGE_SUPPORT_ALPHA
+ if (AlphaIsValid()) {
+ if (aa>255) aa=255; if (aa<0) aa=0; color.rgbReserved=(BYTE) aa;
+ } else
+#endif
+ { //Alpha not supported or no alpha at all
+ color.rgbReserved = 0;
+ }
+ return color;
+ }//switch
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Helper function for GetAreaColorInterpolated.
+ * Adds 'surf' portion of image pixel with color 'color' to (rr,gg,bb,aa).
+ */
+void CxImage::AddAveragingCont(RGBQUAD const &color, float const surf, float &rr, float &gg, float &bb, float &aa)
+{
+ rr+=color.rgbRed*surf;
+ gg+=color.rgbGreen*surf;
+ bb+=color.rgbBlue*surf;
+#if CXIMAGE_SUPPORT_ALPHA
+ aa+=color.rgbReserved*surf;
+#endif
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * This method is similar to GetPixelColorInterpolated, but this method also properly handles
+ * subsampling.
+ * If you need to sample original image with interval of more than 1 pixel (as when shrinking an image),
+ * you should use this method instead of GetPixelColorInterpolated or aliasing will occur.
+ * When area width and height are both less than pixel, this method gets pixel color by interpolating
+ * color of frame center with selected (inMethod) interpolation by calling GetPixelColorInterpolated.
+ * If width and height are more than 1, method calculates color by averaging color of pixels within area.
+ * Interpolation method is not used in this case. Pixel color is interpolated by averaging instead.
+ * If only one of both is more than 1, method uses combination of interpolation and averaging.
+ * Chosen interpolation method is used, but since it is averaged later on, there is little difference
+ * between IM_BILINEAR (perhaps best for this case) and better methods. IM_NEAREST_NEIGHBOUR again
+ * leads to aliasing artifacts.
+ * This method is a bit slower than GetPixelColorInterpolated and when aliasing is not a problem, you should
+ * simply use the later.
+ *
+ * \param xc, yc - center of (rectangular) area
+ * \param w, h - width and height of area
+ * \param inMethod - interpolation method that is used, when interpolation is used (see above)
+ * \param ofMethod - overflow method used when retrieving individual pixel colors
+ * \param rplColor - replacement colour to use, in OM_COLOR
+ *
+ * \author ***bd*** 2.2004
+ */
+RGBQUAD CxImage::GetAreaColorInterpolated(
+ float const xc, float const yc, float const w, float const h,
+ InterpolationMethod const inMethod,
+ OverflowMethod const ofMethod,
+ RGBQUAD* const rplColor)
+{
+ RGBQUAD color; //calculated colour
+
+ if (h<=1 && w<=1) {
+ //both width and height are less than one... we will use interpolation of center point
+ return GetPixelColorInterpolated(xc, yc, inMethod, ofMethod, rplColor);
+ } else {
+ //area is wider and/or taller than one pixel:
+ CxRect2 area(xc-w/2.0f, yc-h/2.0f, xc+w/2.0f, yc+h/2.0f); //area
+ int xi1=(int)(area.botLeft.x+0.49999999f); //low x
+ int yi1=(int)(area.botLeft.y+0.49999999f); //low y
+
+
+ int xi2=(int)(area.topRight.x+0.5f); //top x
+ int yi2=(int)(area.topRight.y+0.5f); //top y (for loops)
+
+ float rr,gg,bb,aa; //red, green, blue and alpha components
+ rr=gg=bb=aa=0;
+ int x,y; //loop counters
+ float s=0; //surface of all pixels
+ float cps; //surface of current crosssection
+ if (h>1 && w>1) {
+ //width and height of area are greater than one pixel, so we can employ "ordinary" averaging
+ CxRect2 intBL, intTR; //bottom left and top right intersection
+ intBL=area.CrossSection(CxRect2(((float)xi1)-0.5f, ((float)yi1)-0.5f, ((float)xi1)+0.5f, ((float)yi1)+0.5f));
+ intTR=area.CrossSection(CxRect2(((float)xi2)-0.5f, ((float)yi2)-0.5f, ((float)xi2)+0.5f, ((float)yi2)+0.5f));
+ float wBL, wTR, hBL, hTR;
+ wBL=intBL.Width(); //width of bottom left pixel-area intersection
+ hBL=intBL.Height(); //height of bottom left...
+ wTR=intTR.Width(); //width of top right...
+ hTR=intTR.Height(); //height of top right...
+
+ AddAveragingCont(GetPixelColorWithOverflow(xi1,yi1,ofMethod,rplColor), wBL*hBL, rr, gg, bb, aa); //bottom left pixel
+ AddAveragingCont(GetPixelColorWithOverflow(xi2,yi1,ofMethod,rplColor), wTR*hBL, rr, gg, bb, aa); //bottom right pixel
+ AddAveragingCont(GetPixelColorWithOverflow(xi1,yi2,ofMethod,rplColor), wBL*hTR, rr, gg, bb, aa); //top left pixel
+ AddAveragingCont(GetPixelColorWithOverflow(xi2,yi2,ofMethod,rplColor), wTR*hTR, rr, gg, bb, aa); //top right pixel
+ //bottom and top row
+ for (x=xi1+1; x255) rr=255; if (rr<0) rr=0; color.rgbRed=(BYTE) rr;
+ if (gg>255) gg=255; if (gg<0) gg=0; color.rgbGreen=(BYTE) gg;
+ if (bb>255) bb=255; if (bb<0) bb=0; color.rgbBlue=(BYTE) bb;
+#if CXIMAGE_SUPPORT_ALPHA
+ if (AlphaIsValid()) {
+ if (aa>255) aa=255; if (aa<0) aa=0; color.rgbReserved=(BYTE) aa;
+ }//if
+#endif
+ }//if
+ return color;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBSpline(const float x)
+{
+ if (x>2.0f) return 0.0f;
+ // thanks to Kristian Kratzenstein
+ float a, b, c, d;
+ float xm1 = x - 1.0f; // Was calculatet anyway cause the "if((x-1.0f) < 0)"
+ float xp1 = x + 1.0f;
+ float xp2 = x + 2.0f;
+
+ if ((xp2) <= 0.0f) a = 0.0f; else a = xp2*xp2*xp2; // Only float, not float -> double -> float
+ if ((xp1) <= 0.0f) b = 0.0f; else b = xp1*xp1*xp1;
+ if (x <= 0) c = 0.0f; else c = x*x*x;
+ if ((xm1) <= 0.0f) d = 0.0f; else d = xm1*xm1*xm1;
+
+ return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d)));
+
+ /* equivalent
+ if (x < -2.0)
+ return(0.0f);
+ if (x < -1.0)
+ return((2.0f+x)*(2.0f+x)*(2.0f+x)*0.16666666666666666667f);
+ if (x < 0.0)
+ return((4.0f+x*x*(-6.0f-3.0f*x))*0.16666666666666666667f);
+ if (x < 1.0)
+ return((4.0f+x*x*(-6.0f+3.0f*x))*0.16666666666666666667f);
+ if (x < 2.0)
+ return((2.0f-x)*(2.0f-x)*(2.0f-x)*0.16666666666666666667f);
+ return(0.0f);
+ */
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Bilinear interpolation kernel:
+ \verbatim
+ /
+ | 1-t , if 0 <= t <= 1
+ h(t) = | t+1 , if -1 <= t < 0
+ | 0 , otherwise
+ \
+ \endverbatim
+ * ***bd*** 2.2004
+ */
+float CxImage::KernelLinear(const float t)
+{
+// if (0<=t && t<=1) return 1-t;
+// if (-1<=t && t<0) return 1+t;
+// return 0;
+
+ //
+ if (t < -1.0f)
+ return 0.0f;
+ if (t < 0.0f)
+ return 1.0f+t;
+ if (t < 1.0f)
+ return 1.0f-t;
+ return 0.0f;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Bicubic interpolation kernel (a=-1):
+ \verbatim
+ /
+ | 1-2|t|**2+|t|**3 , if |t| < 1
+ h(t) = | 4-8|t|+5|t|**2-|t|**3 , if 1<=|t|<2
+ | 0 , otherwise
+ \
+ \endverbatim
+ * ***bd*** 2.2004
+ */
+float CxImage::KernelCubic(const float t)
+{
+ float abs_t = (float)fabs(t);
+ float abs_t_sq = abs_t * abs_t;
+ if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t;
+ if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t;
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Bicubic kernel (for a=-1 it is the same as BicubicKernel):
+ \verbatim
+ /
+ | (a+2)|t|**3 - (a+3)|t|**2 + 1 , |t| <= 1
+ h(t) = | a|t|**3 - 5a|t|**2 + 8a|t| - 4a , 1 < |t| <= 2
+ | 0 , otherwise
+ \
+ \endverbatim
+ * Often used values for a are -1 and -1/2.
+ */
+float CxImage::KernelGeneralizedCubic(const float t, const float a)
+{
+ float abs_t = (float)fabs(t);
+ float abs_t_sq = abs_t * abs_t;
+ if (abs_t<1) return (a+2)*abs_t_sq*abs_t - (a+3)*abs_t_sq + 1;
+ if (abs_t<2) return a*abs_t_sq*abs_t - 5*a*abs_t_sq + 8*a*abs_t - 4*a;
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Lanczos windowed sinc interpolation kernel with radius r.
+ \verbatim
+ /
+ h(t) = | sinc(t)*sinc(t/r) , if |t| r) return 0;
+ if (t==0) return 1;
+ float pit=PI*t;
+ float pitd=pit/r;
+ return (float)((sin(pit)/pit) * (sin(pitd)/pitd));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBox(const float x)
+{
+ if (x < -0.5f)
+ return 0.0f;
+ if (x < 0.5f)
+ return 1.0f;
+ return 0.0f;
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelHermite(const float x)
+{
+ if (x < -1.0f)
+ return 0.0f;
+ if (x < 0.0f)
+ return (-2.0f*x-3.0f)*x*x+1.0f;
+ if (x < 1.0f)
+ return (2.0f*x-3.0f)*x*x+1.0f;
+ return 0.0f;
+// if (fabs(x)>1) return 0.0f;
+// return(0.5f+0.5f*(float)cos(PI*x));
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelHanning(const float x)
+{
+ if (fabs(x)>1) return 0.0f;
+ return (0.5f+0.5f*(float)cos(PI*x))*((float)sin(PI*x)/(PI*x));
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelHamming(const float x)
+{
+ if (x < -1.0f)
+ return 0.0f;
+ if (x < 0.0f)
+ return 0.92f*(-2.0f*x-3.0f)*x*x+1.0f;
+ if (x < 1.0f)
+ return 0.92f*(2.0f*x-3.0f)*x*x+1.0f;
+ return 0.0f;
+// if (fabs(x)>1) return 0.0f;
+// return(0.54f+0.46f*(float)cos(PI*x));
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelSinc(const float x)
+{
+ if (x == 0.0)
+ return(1.0);
+ return((float)sin(PI*x)/(PI*x));
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBlackman(const float x)
+{
+ //if (fabs(x)>1) return 0.0f;
+ return (0.42f+0.5f*(float)cos(PI*x)+0.08f*(float)cos(2.0f*PI*x));
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBessel_J1(const float x)
+{
+ double p, q;
+
+ register long i;
+
+ static const double
+ Pone[] =
+ {
+ 0.581199354001606143928050809e+21,
+ -0.6672106568924916298020941484e+20,
+ 0.2316433580634002297931815435e+19,
+ -0.3588817569910106050743641413e+17,
+ 0.2908795263834775409737601689e+15,
+ -0.1322983480332126453125473247e+13,
+ 0.3413234182301700539091292655e+10,
+ -0.4695753530642995859767162166e+7,
+ 0.270112271089232341485679099e+4
+ },
+ Qone[] =
+ {
+ 0.11623987080032122878585294e+22,
+ 0.1185770712190320999837113348e+20,
+ 0.6092061398917521746105196863e+17,
+ 0.2081661221307607351240184229e+15,
+ 0.5243710262167649715406728642e+12,
+ 0.1013863514358673989967045588e+10,
+ 0.1501793594998585505921097578e+7,
+ 0.1606931573481487801970916749e+4,
+ 0.1e+1
+ };
+
+ p = Pone[8];
+ q = Qone[8];
+ for (i=7; i >= 0; i--)
+ {
+ p = p*x*x+Pone[i];
+ q = q*x*x+Qone[i];
+ }
+ return (float)(p/q);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBessel_P1(const float x)
+{
+ double p, q;
+
+ register long i;
+
+ static const double
+ Pone[] =
+ {
+ 0.352246649133679798341724373e+5,
+ 0.62758845247161281269005675e+5,
+ 0.313539631109159574238669888e+5,
+ 0.49854832060594338434500455e+4,
+ 0.2111529182853962382105718e+3,
+ 0.12571716929145341558495e+1
+ },
+ Qone[] =
+ {
+ 0.352246649133679798068390431e+5,
+ 0.626943469593560511888833731e+5,
+ 0.312404063819041039923015703e+5,
+ 0.4930396490181088979386097e+4,
+ 0.2030775189134759322293574e+3,
+ 0.1e+1
+ };
+
+ p = Pone[5];
+ q = Qone[5];
+ for (i=4; i >= 0; i--)
+ {
+ p = p*(8.0/x)*(8.0/x)+Pone[i];
+ q = q*(8.0/x)*(8.0/x)+Qone[i];
+ }
+ return (float)(p/q);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBessel_Q1(const float x)
+{
+ double p, q;
+
+ register long i;
+
+ static const double
+ Pone[] =
+ {
+ 0.3511751914303552822533318e+3,
+ 0.7210391804904475039280863e+3,
+ 0.4259873011654442389886993e+3,
+ 0.831898957673850827325226e+2,
+ 0.45681716295512267064405e+1,
+ 0.3532840052740123642735e-1
+ },
+ Qone[] =
+ {
+ 0.74917374171809127714519505e+4,
+ 0.154141773392650970499848051e+5,
+ 0.91522317015169922705904727e+4,
+ 0.18111867005523513506724158e+4,
+ 0.1038187585462133728776636e+3,
+ 0.1e+1
+ };
+
+ p = Pone[5];
+ q = Qone[5];
+ for (i=4; i >= 0; i--)
+ {
+ p = p*(8.0/x)*(8.0/x)+Pone[i];
+ q = q*(8.0/x)*(8.0/x)+Qone[i];
+ }
+ return (float)(p/q);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBessel_Order1(float x)
+{
+ float p, q;
+
+ if (x == 0.0)
+ return (0.0f);
+ p = x;
+ if (x < 0.0)
+ x=(-x);
+ if (x < 8.0)
+ return(p*KernelBessel_J1(x));
+ q = (float)sqrt(2.0f/(PI*x))*(float)(KernelBessel_P1(x)*(1.0f/sqrt(2.0f)*(sin(x)-cos(x)))-8.0f/x*KernelBessel_Q1(x)*
+ (-1.0f/sqrt(2.0f)*(sin(x)+cos(x))));
+ if (p < 0.0f)
+ q = (-q);
+ return (q);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelBessel(const float x)
+{
+ if (x == 0.0f)
+ return(PI/4.0f);
+ return(KernelBessel_Order1(PI*x)/(2.0f*x));
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelGaussian(const float x)
+{
+ return (float)(exp(-2.0f*x*x)*0.79788456080287f/*sqrt(2.0f/PI)*/);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelQuadratic(const float x)
+{
+ if (x < -1.5f)
+ return(0.0f);
+ if (x < -0.5f)
+ return(0.5f*(x+1.5f)*(x+1.5f));
+ if (x < 0.5f)
+ return(0.75f-x*x);
+ if (x < 1.5f)
+ return(0.5f*(x-1.5f)*(x-1.5f));
+ return(0.0f);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelMitchell(const float x)
+{
+#define KM_B (1.0f/3.0f)
+#define KM_C (1.0f/3.0f)
+#define KM_P0 (( 6.0f - 2.0f * KM_B ) / 6.0f)
+#define KM_P2 ((-18.0f + 12.0f * KM_B + 6.0f * KM_C) / 6.0f)
+#define KM_P3 (( 12.0f - 9.0f * KM_B - 6.0f * KM_C) / 6.0f)
+#define KM_Q0 (( 8.0f * KM_B + 24.0f * KM_C) / 6.0f)
+#define KM_Q1 ((-12.0f * KM_B - 48.0f * KM_C) / 6.0f)
+#define KM_Q2 (( 6.0f * KM_B + 30.0f * KM_C) / 6.0f)
+#define KM_Q3 (( -1.0f * KM_B - 6.0f * KM_C) / 6.0f)
+
+ if (x < -2.0)
+ return(0.0f);
+ if (x < -1.0)
+ return(KM_Q0-x*(KM_Q1-x*(KM_Q2-x*KM_Q3)));
+ if (x < 0.0f)
+ return(KM_P0+x*x*(KM_P2-x*KM_P3));
+ if (x < 1.0f)
+ return(KM_P0+x*x*(KM_P2+x*KM_P3));
+ if (x < 2.0f)
+ return(KM_Q0+x*(KM_Q1+x*(KM_Q2+x*KM_Q3)));
+ return(0.0f);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelCatrom(const float x)
+{
+ if (x < -2.0)
+ return(0.0f);
+ if (x < -1.0)
+ return(0.5f*(4.0f+x*(8.0f+x*(5.0f+x))));
+ if (x < 0.0)
+ return(0.5f*(2.0f+x*x*(-5.0f-3.0f*x)));
+ if (x < 1.0)
+ return(0.5f*(2.0f+x*x*(-5.0f+3.0f*x)));
+ if (x < 2.0)
+ return(0.5f*(4.0f+x*(-8.0f+x*(5.0f-x))));
+ return(0.0f);
+}
+////////////////////////////////////////////////////////////////////////////////
+float CxImage::KernelPower(const float x, const float a)
+{
+ if (fabs(x)>1) return 0.0f;
+ return (1.0f - (float)fabs(pow(x,a)));
+}
+////////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/CxImage/ximaiter.h b/CxImage/ximaiter.h
new file mode 100644
index 00000000..9788919b
--- /dev/null
+++ b/CxImage/ximaiter.h
@@ -0,0 +1,253 @@
+/*
+ * File: ImaIter.h
+ * Purpose: Declaration of the Platform Independent Image Base Class
+ * Author: Alejandro Aguilar Sierra
+ * Created: 1995
+ * Copyright: (c) 1995, Alejandro Aguilar Sierra
+ *
+ * 07/08/2001 Davide Pizzolato - www.xdp.it
+ * - removed slow loops
+ * - added safe checks
+ *
+ * Permission is given by the author to freely redistribute and include
+ * this code in any program as long as this credit is given where due.
+ *
+ * COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+ * OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+ * CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+ * THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+ * SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+ * PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.
+ *
+ * Use at your own risk!
+ * ==========================================================
+ */
+
+#if !defined(__ImaIter_h)
+#define __ImaIter_h
+
+#include "ximage.h"
+#include "ximadef.h"
+
+class CImageIterator
+{
+friend class CxImage;
+protected:
+ int Itx, Ity; // Counters
+ int Stepx, Stepy;
+ BYTE* IterImage; // Image pointer
+ CxImage *ima;
+public:
+ // Constructors
+ CImageIterator ( void );
+ CImageIterator ( CxImage *image );
+ operator CxImage* ();
+
+ // Iterators
+ BOOL ItOK ();
+ void Reset ();
+ void Upset ();
+ void SetRow(BYTE *buf, int n);
+ void GetRow(BYTE *buf, int n);
+ BYTE GetByte( ) { return IterImage[Itx]; }
+ void SetByte(BYTE b) { IterImage[Itx] = b; }
+ BYTE* GetRow(void);
+ BYTE* GetRow(int n);
+ BOOL NextRow();
+ BOOL PrevRow();
+ BOOL NextByte();
+ BOOL PrevByte();
+
+ void SetSteps(int x, int y=0) { Stepx = x; Stepy = y; }
+ void GetSteps(int *x, int *y) { *x = Stepx; *y = Stepy; }
+ BOOL NextStep();
+ BOOL PrevStep();
+
+ void SetY(int y); /* AD - for interlace */
+ int GetY() {return Ity;}
+ BOOL GetCol(BYTE* pCol, DWORD x);
+ BOOL SetCol(BYTE* pCol, DWORD x);
+};
+
+/////////////////////////////////////////////////////////////////////
+inline
+CImageIterator::CImageIterator(void)
+{
+ ima = 0;
+ IterImage = 0;
+ Itx = Ity = 0;
+ Stepx = Stepy = 0;
+}
+/////////////////////////////////////////////////////////////////////
+inline
+CImageIterator::CImageIterator(CxImage *imageImpl): ima(imageImpl)
+{
+ if (ima) IterImage = ima->GetBits();
+ Itx = Ity = 0;
+ Stepx = Stepy = 0;
+}
+/////////////////////////////////////////////////////////////////////
+inline
+CImageIterator::operator CxImage* ()
+{
+ return ima;
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::ItOK ()
+{
+ if (ima) return ima->IsInside(Itx, Ity);
+ else return FALSE;
+}
+/////////////////////////////////////////////////////////////////////
+inline void CImageIterator::Reset()
+{
+ if (ima) IterImage = ima->GetBits();
+ else IterImage=0;
+ Itx = Ity = 0;
+}
+/////////////////////////////////////////////////////////////////////
+inline void CImageIterator::Upset()
+{
+ Itx = 0;
+ Ity = ima->GetHeight()-1;
+ IterImage = ima->GetBits() + ima->GetEffWidth()*(ima->GetHeight()-1);
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::NextRow()
+{
+ if (++Ity >= (int)ima->GetHeight()) return 0;
+ IterImage += ima->GetEffWidth();
+ return 1;
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::PrevRow()
+{
+ if (--Ity < 0) return 0;
+ IterImage -= ima->GetEffWidth();
+ return 1;
+}
+/* AD - for interlace */
+inline void CImageIterator::SetY(int y)
+{
+ if ((y < 0) || (y > (int)ima->GetHeight())) return;
+ Ity = y;
+ IterImage = ima->GetBits() + ima->GetEffWidth()*y;
+}
+/////////////////////////////////////////////////////////////////////
+inline void CImageIterator::SetRow(BYTE *buf, int n)
+{
+ if (n<0) n = (int)ima->GetEffWidth();
+ else n = min(n,(int)ima->GetEffWidth());
+
+ if ((IterImage!=NULL)&&(buf!=NULL)&&(n>0)) memcpy(IterImage,buf,n);
+}
+/////////////////////////////////////////////////////////////////////
+inline void CImageIterator::GetRow(BYTE *buf, int n)
+{
+ if ((IterImage!=NULL)&&(buf!=NULL)&&(n>0))
+ memcpy(buf,IterImage,min(n,(int)ima->GetEffWidth()));
+}
+/////////////////////////////////////////////////////////////////////
+inline BYTE* CImageIterator::GetRow()
+{
+ return IterImage;
+}
+/////////////////////////////////////////////////////////////////////
+inline BYTE* CImageIterator::GetRow(int n)
+{
+ SetY(n);
+ return IterImage;
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::NextByte()
+{
+ if (++Itx < (int)ima->GetEffWidth()) return 1;
+ else
+ if (++Ity < (int)ima->GetHeight()){
+ IterImage += ima->GetEffWidth();
+ Itx = 0;
+ return 1;
+ } else
+ return 0;
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::PrevByte()
+{
+ if (--Itx >= 0) return 1;
+ else
+ if (--Ity >= 0){
+ IterImage -= ima->GetEffWidth();
+ Itx = 0;
+ return 1;
+ } else
+ return 0;
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::NextStep()
+{
+ Itx += Stepx;
+ if (Itx < (int)ima->GetEffWidth()) return 1;
+ else {
+ Ity += Stepy;
+ if (Ity < (int)ima->GetHeight()){
+ IterImage += ima->GetEffWidth();
+ Itx = 0;
+ return 1;
+ } else
+ return 0;
+ }
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::PrevStep()
+{
+ Itx -= Stepx;
+ if (Itx >= 0) return 1;
+ else {
+ Ity -= Stepy;
+ if (Ity >= 0 && Ity < (int)ima->GetHeight()) {
+ IterImage -= ima->GetEffWidth();
+ Itx = 0;
+ return 1;
+ } else
+ return 0;
+ }
+}
+/////////////////////////////////////////////////////////////////////
+inline BOOL CImageIterator::GetCol(BYTE* pCol, DWORD x)
+{
+ if ((pCol==0)||(ima->GetBpp()<8)||(x>=ima->GetWidth()))
+ return 0;
+ DWORD h = ima->GetHeight();
+ //DWORD line = ima->GetEffWidth();
+ BYTE bytes = (BYTE)(ima->GetBpp()>>3);
+ BYTE* pSrc;
+ for (DWORD y=0;yGetBits(y) + x*bytes;
+ for (BYTE w=0;wGetBpp()<8)||(x>=ima->GetWidth()))
+ return 0;
+ DWORD h = ima->GetHeight();
+ //DWORD line = ima->GetEffWidth();
+ BYTE bytes = (BYTE)(ima->GetBpp()>>3);
+ BYTE* pSrc;
+ for (DWORD y=0;yGetBits(y) + x*bytes;
+ for (BYTE w=0;w>8);
+ c.rgbGreen = (BYTE)((c.rgbGreen * a + a1 * info.nBkgndColor.rgbGreen)>>8);
+ c.rgbRed = (BYTE)((c.rgbRed * a + a1 * info.nBkgndColor.rgbRed)>>8);
+ BlindSetPixelColor(x,y,c);
+ }
+ }
+ AlphaDelete();
+ } else {
+ CxImage tmp(head.biWidth,head.biHeight,24);
+ if (!tmp.IsValid()){
+ strcpy(info.szLastError,tmp.GetLastError());
+ return;
+ }
+
+ for(long y=0; y>8);
+ c.rgbGreen = (BYTE)((c.rgbGreen * a + a1 * info.nBkgndColor.rgbGreen)>>8);
+ c.rgbRed = (BYTE)((c.rgbRed * a + a1 * info.nBkgndColor.rgbRed)>>8);
+ tmp.BlindSetPixelColor(x,y,c);
+ }
+ }
+ Transfer(tmp);
+ }
+ return;
+}
+////////////////////////////////////////////////////////////////////////////////
+bool CxImage::AlphaFlip()
+{
+ if (!pAlpha) return false;
+
+ BYTE *buff = (BYTE*)malloc(head.biWidth);
+ if (!buff) return false;
+
+ BYTE *iSrc,*iDst;
+ iSrc = pAlpha + (head.biHeight-1)*head.biWidth;
+ iDst = pAlpha;
+ for (long i=0; i<(head.biHeight/2); ++i)
+ {
+ memcpy(buff, iSrc, head.biWidth);
+ memcpy(iSrc, iDst, head.biWidth);
+ memcpy(iDst, buff, head.biWidth);
+ iSrc-=head.biWidth;
+ iDst+=head.biWidth;
+ }
+
+ free(buff);
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+bool CxImage::AlphaMirror()
+{
+ if (!pAlpha) return false;
+ BYTE* pAlpha2 = (BYTE*)malloc(head.biWidth * head.biHeight);
+ if (!pAlpha2) return false;
+ BYTE *iSrc,*iDst;
+ long wdt=head.biWidth-1;
+ iSrc=pAlpha + wdt;
+ iDst=pAlpha2;
+ for(long y=0; y < head.biHeight; y++){
+ for(long x=0; x <= wdt; x++)
+ *(iDst+x)=*(iSrc-x);
+ iSrc+=head.biWidth;
+ iDst+=head.biWidth;
+ }
+ free(pAlpha);
+ pAlpha=pAlpha2;
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Exports the alpha channel in a 8bpp grayscale image.
+ */
+bool CxImage::AlphaSplit(CxImage *dest)
+{
+ if (!pAlpha || !dest) return false;
+
+ CxImage tmp(head.biWidth,head.biHeight,8);
+ if (!tmp.IsValid()){
+ strcpy(info.szLastError,tmp.GetLastError());
+ return false;
+ }
+
+ for(long y=0; yTransfer(tmp);
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Exports the alpha palette channel in a 8bpp grayscale image.
+ */
+bool CxImage::AlphaPaletteSplit(CxImage *dest)
+{
+ if (!AlphaPaletteIsValid() || !dest) return false;
+
+ CxImage tmp(head.biWidth,head.biHeight,8);
+ if (!tmp.IsValid()){
+ strcpy(info.szLastError,tmp.GetLastError());
+ return false;
+ }
+
+ for(long y=0; yTransfer(tmp);
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Merge in the alpha layer the transparent color mask
+ * (previously set with SetTransColor or SetTransIndex)
+ */
+bool CxImage::AlphaFromTransparency()
+{
+ if (!IsValid() || !IsTransparent())
+ return false;
+
+ AlphaCreate();
+
+ for(long y=0; y=head.biWidth)||(y>=head.biHeight)) {
+ if (info.nBkgndIndex >= 0) return (BYTE)info.nBkgndIndex;
+ else return *info.pImage;
+ }
+ if (head.biBitCount==8){
+ return info.pImage[y*info.dwEffWidth + x];
+ } else {
+ BYTE pos;
+ BYTE iDst= info.pImage[y*info.dwEffWidth + (x*head.biBitCount >> 3)];
+ if (head.biBitCount==4){
+ pos = (BYTE)(4*(1-x%2));
+ iDst &= (0x0F<> pos);
+ } else if (head.biBitCount==1){
+ pos = (BYTE)(7-x%8);
+ iDst &= (0x01<> pos);
+ }
+ }
+ return 0;
+}
+////////////////////////////////////////////////////////////////////////////////
+BYTE CxImage::BlindGetPixelIndex(const long x,const long y)
+{
+#ifdef _DEBUG
+ if ((pDib==NULL) || (head.biClrUsed==0) || !IsInside(x,y))
+ #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
+ throw 0;
+ #else
+ return 0;
+ #endif
+#endif
+
+ if (head.biBitCount==8){
+ return info.pImage[y*info.dwEffWidth + x];
+ } else {
+ BYTE pos;
+ BYTE iDst= info.pImage[y*info.dwEffWidth + (x*head.biBitCount >> 3)];
+ if (head.biBitCount==4){
+ pos = (BYTE)(4*(1-x%2));
+ iDst &= (0x0F<> pos);
+ } else if (head.biBitCount==1){
+ pos = (BYTE)(7-x%8);
+ iDst &= (0x01<> pos);
+ }
+ }
+ return 0;
+}
+////////////////////////////////////////////////////////////////////////////////
+RGBQUAD CxImage::GetPixelColor(long x,long y, bool bGetAlpha)
+{
+// RGBQUAD rgb={0,0,0,0};
+ RGBQUAD rgb=info.nBkgndColor; //
+ if ((pDib==NULL)||(x<0)||(y<0)||
+ (x>=head.biWidth)||(y>=head.biHeight)){
+ if (info.nBkgndIndex >= 0){
+ if (head.biBitCount<24) return GetPaletteColor((BYTE)info.nBkgndIndex);
+ else return info.nBkgndColor;
+ } else if (pDib) return GetPixelColor(0,0);
+ return rgb;
+ }
+
+ if (head.biClrUsed){
+ rgb = GetPaletteColor(BlindGetPixelIndex(x,y));
+ } else {
+ BYTE* iDst = info.pImage + y*info.dwEffWidth + x*3;
+ rgb.rgbBlue = *iDst++;
+ rgb.rgbGreen= *iDst++;
+ rgb.rgbRed = *iDst;
+ }
+#if CXIMAGE_SUPPORT_ALPHA
+ if (pAlpha && bGetAlpha) rgb.rgbReserved = BlindAlphaGet(x,y);
+#else
+ rgb.rgbReserved = 0;
+#endif //CXIMAGE_SUPPORT_ALPHA
+ return rgb;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * This is (a bit) faster version of GetPixelColor.
+ * It tests bounds only in debug mode (_DEBUG defined).
+ *
+ * It is an error to request out-of-borders pixel with this method.
+ * In DEBUG mode an exception will be thrown, and data will be violated in non-DEBUG mode.
+ * \author ***bd*** 2.2004
+ */
+RGBQUAD CxImage::BlindGetPixelColor(const long x,const long y, bool bGetAlpha)
+{
+ RGBQUAD rgb;
+#ifdef _DEBUG
+ if ((pDib==NULL) || !IsInside(x,y))
+ #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
+ throw 0;
+ #else
+ {rgb.rgbReserved = 0; return rgb;}
+ #endif
+#endif
+
+ if (head.biClrUsed){
+ rgb = GetPaletteColor(BlindGetPixelIndex(x,y));
+ } else {
+ BYTE* iDst = info.pImage + y*info.dwEffWidth + x*3;
+ rgb.rgbBlue = *iDst++;
+ rgb.rgbGreen= *iDst++;
+ rgb.rgbRed = *iDst;
+ rgb.rgbReserved = 0; //needed for images without alpha layer
+ }
+#if CXIMAGE_SUPPORT_ALPHA
+ if (pAlpha && bGetAlpha) rgb.rgbReserved = BlindAlphaGet(x,y);
+#else
+ rgb.rgbReserved = 0;
+#endif //CXIMAGE_SUPPORT_ALPHA
+ return rgb;
+}
+////////////////////////////////////////////////////////////////////////////////
+BYTE CxImage::GetPixelGray(long x, long y)
+{
+ RGBQUAD color = GetPixelColor(x,y);
+ return (BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue);
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::BlindSetPixelIndex(long x,long y,BYTE i)
+{
+#ifdef _DEBUG
+ if ((pDib==NULL)||(head.biClrUsed==0)||
+ (x<0)||(y<0)||(x>=head.biWidth)||(y>=head.biHeight))
+ #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
+ throw 0;
+ #else
+ return;
+ #endif
+#endif
+
+ if (head.biBitCount==8){
+ info.pImage[y*info.dwEffWidth + x]=i;
+ return;
+ } else {
+ BYTE pos;
+ BYTE* iDst= info.pImage + y*info.dwEffWidth + (x*head.biBitCount >> 3);
+ if (head.biBitCount==4){
+ pos = (BYTE)(4*(1-x%2));
+ *iDst &= ~(0x0F<=head.biWidth)||(y>=head.biHeight)) return ;
+
+ if (head.biBitCount==8){
+ info.pImage[y*info.dwEffWidth + x]=i;
+ return;
+ } else {
+ BYTE pos;
+ BYTE* iDst= info.pImage + y*info.dwEffWidth + (x*head.biBitCount >> 3);
+ if (head.biBitCount==4){
+ pos = (BYTE)(4*(1-x%2));
+ *iDst &= ~(0x0F<=head.biWidth)||(y>=head.biHeight))
+ #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
+ throw 0;
+ #else
+ return;
+ #endif
+#endif
+ if (head.biClrUsed)
+ BlindSetPixelIndex(x,y,GetNearestIndex(c));
+ else {
+ BYTE* iDst = info.pImage + y*info.dwEffWidth + x*3;
+ *iDst++ = c.rgbBlue;
+ *iDst++ = c.rgbGreen;
+ *iDst = c.rgbRed;
+ }
+#if CXIMAGE_SUPPORT_ALPHA
+ if (bSetAlpha) AlphaSet(x,y,c.rgbReserved);
+#endif //CXIMAGE_SUPPORT_ALPHA
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::SetPixelColor(long x,long y,RGBQUAD c, bool bSetAlpha)
+{
+ if ((pDib==NULL)||(x<0)||(y<0)||
+ (x>=head.biWidth)||(y>=head.biHeight)) return;
+ if (head.biClrUsed)
+ BlindSetPixelIndex(x,y,GetNearestIndex(c));
+ else {
+ BYTE* iDst = info.pImage + y*info.dwEffWidth + x*3;
+ *iDst++ = c.rgbBlue;
+ *iDst++ = c.rgbGreen;
+ *iDst = c.rgbRed;
+ }
+#if CXIMAGE_SUPPORT_ALPHA
+ if (bSetAlpha) AlphaSet(x,y,c.rgbReserved);
+#endif //CXIMAGE_SUPPORT_ALPHA
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Blends the current pixel color with a new color.
+ * \param x,y = pixel
+ * \param c = new color
+ * \param blend = can be from 0 (no effect) to 1 (full effect).
+ * \param bSetAlpha = if true, blends also the alpha component stored in c.rgbReserved
+ */
+void CxImage::BlendPixelColor(long x,long y,RGBQUAD c, float blend, bool bSetAlpha)
+{
+ if ((pDib==NULL)||(x<0)||(y<0)||
+ (x>=head.biWidth)||(y>=head.biHeight)) return;
+
+ int a0 = (int)(256*blend);
+ int a1 = 256 - a0;
+
+ RGBQUAD c0 = BlindGetPixelColor(x,y);
+ c.rgbRed = (BYTE)((c.rgbRed * a0 + c0.rgbRed * a1)>>8);
+ c.rgbBlue = (BYTE)((c.rgbBlue * a0 + c0.rgbBlue * a1)>>8);
+ c.rgbGreen = (BYTE)((c.rgbGreen * a0 + c0.rgbGreen * a1)>>8);
+
+ if (head.biClrUsed)
+ BlindSetPixelIndex(x,y,GetNearestIndex(c));
+ else {
+ BYTE* iDst = info.pImage + y*info.dwEffWidth + x*3;
+ *iDst++ = c.rgbBlue;
+ *iDst++ = c.rgbGreen;
+ *iDst = c.rgbRed;
+#if CXIMAGE_SUPPORT_ALPHA
+ if (bSetAlpha) AlphaSet(x,y,c.rgbReserved);
+#endif //CXIMAGE_SUPPORT_ALPHA
+ }
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the best palette index that matches a specified color.
+ */
+BYTE CxImage::GetNearestIndex(RGBQUAD c)
+{
+ if ((pDib==NULL)||(head.biClrUsed==0)) return 0;
+
+ // check matching with the previous result
+ if (info.last_c_isvalid && (*(long*)&info.last_c == *(long*)&c)) return info.last_c_index;
+ info.last_c = c;
+ info.last_c_isvalid = true;
+
+ BYTE* iDst = (BYTE*)(pDib) + sizeof(BITMAPINFOHEADER);
+ long distance=200000;
+ int i,j = 0;
+ long k,l;
+ int m = (int)(head.biClrImportant==0 ? head.biClrUsed : head.biClrImportant);
+ for(i=0,l=0;i100) perc=100;
+ for(i=0;i=0){
+ if (head.biClrUsed){
+ if (GetPixelIndex(x,y) == info.nBkgndIndex) return true;
+ } else {
+ RGBQUAD ct = info.nBkgndColor;
+ RGBQUAD c = GetPixelColor(x,y,false);
+ if (*(long*)&c==*(long*)&ct) return true;
+ }
+ }
+
+#if CXIMAGE_SUPPORT_ALPHA
+ if (pAlpha) return AlphaGet(x,y)==0;
+#endif
+
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+bool CxImage::GetTransparentMask(CxImage* iDst)
+{
+ if (!pDib) return false;
+
+ CxImage tmp;
+ tmp.Create(head.biWidth, head.biHeight, 1, GetType());
+ tmp.SetStdPalette();
+ tmp.Clear(0);
+
+ for(long y=0; yTransfer(tmp);
+ else Transfer(tmp);
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks if image has the same palette, if any.
+ * \param img = image to compare.
+ * \param bCheckAlpha = check also the rgbReserved field.
+ */
+bool CxImage::IsSamePalette(CxImage &img, bool bCheckAlpha)
+{
+ if (head.biClrUsed != img.head.biClrUsed)
+ return false;
+ if (head.biClrUsed == 0)
+ return false;
+
+ RGBQUAD c1,c2;
+ for (DWORD n=0; n256) {
+ head.biClrImportant = 0;
+ return;
+ }
+
+ switch(head.biBitCount){
+ case 1:
+ head.biClrImportant = min(ncolors,2);
+ break;
+ case 4:
+ head.biClrImportant = min(ncolors,16);
+ break;
+ case 8:
+ head.biClrImportant = ncolors;
+ break;
+ }
+ return;
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns pointer to pixel. Currently implemented only for truecolor images.
+ *
+ * \param x,y - coordinates
+ *
+ * \return pointer to first byte of pixel data
+ *
+ * \author ***bd*** 2.2004
+ */
+void* CxImage::BlindGetPixelPointer(const long x, const long y)
+{
+#ifdef _DEBUG
+ if ((pDib==NULL) || !IsInside(x,y))
+ #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING
+ throw 0;
+ #else
+ return 0;
+ #endif
+#endif
+ if (!IsIndexed())
+ return info.pImage + y*info.dwEffWidth + x*3;
+ else
+ return 0;
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::DrawLine(int StartX, int EndX, int StartY, int EndY, COLORREF cr)
+{
+ DrawLine(StartX, EndX, StartY, EndY, RGBtoRGBQUAD(cr));
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImage::DrawLine(int StartX, int EndX, int StartY, int EndY, RGBQUAD color, bool bSetAlpha)
+{
+ if (!pDib) return;
+ //////////////////////////////////////////////////////
+ // Draws a line using the Bresenham line algorithm
+ // Thanks to Jordan DeLozier
+ //////////////////////////////////////////////////////
+ int x1 = StartX;
+ int y1 = StartY;
+ int x = x1; // Start x off at the first pixel
+ int y = y1; // Start y off at the first pixel
+ int x2 = EndX;
+ int y2 = EndY;
+
+ int xinc1,xinc2,yinc1,yinc2; // Increasing values
+ int den, num, numadd,numpixels;
+ int deltax = abs(x2 - x1); // The difference between the x's
+ int deltay = abs(y2 - y1); // The difference between the y's
+
+ // Get Increasing Values
+ if (x2 >= x1) { // The x-values are increasing
+ xinc1 = 1;
+ xinc2 = 1;
+ } else { // The x-values are decreasing
+ xinc1 = -1;
+ xinc2 = -1;
+ }
+
+ if (y2 >= y1) { // The y-values are increasing
+ yinc1 = 1;
+ yinc2 = 1;
+ } else { // The y-values are decreasing
+ yinc1 = -1;
+ yinc2 = -1;
+ }
+
+ // Actually draw the line
+ if (deltax >= deltay) // There is at least one x-value for every y-value
+ {
+ xinc1 = 0; // Don't change the x when numerator >= denominator
+ yinc2 = 0; // Don't change the y for every iteration
+ den = deltax;
+ num = deltax / 2;
+ numadd = deltay;
+ numpixels = deltax; // There are more x-values than y-values
+ }
+ else // There is at least one y-value for every x-value
+ {
+ xinc2 = 0; // Don't change the x for every iteration
+ yinc1 = 0; // Don't change the y when numerator >= denominator
+ den = deltay;
+ num = deltay / 2;
+ numadd = deltax;
+ numpixels = deltay; // There are more y-values than x-values
+ }
+
+ for (int curpixel = 0; curpixel <= numpixels; curpixel++)
+ {
+ // Draw the current pixel
+ SetPixelColor(x,y,color,bSetAlpha);
+
+ num += numadd; // Increase the numerator by the top of the fraction
+ if (num >= den) // Check if numerator >= denominator
+ {
+ num -= den; // Calculate the new numerator value
+ x += xinc1; // Change the x as appropriate
+ y += yinc1; // Change the y as appropriate
+ }
+ x += xinc2; // Change the x as appropriate
+ y += yinc2; // Change the y as appropriate
+ }
+}
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets a palette with standard colors for 1, 4 and 8 bpp images.
+ */
+void CxImage::SetStdPalette()
+{
+ if (!pDib) return;
+ switch (head.biBitCount){
+ case 8:
+ {
+ const BYTE pal256[1024] = {0,0,0,0,0,0,128,0,0,128,0,0,0,128,128,0,128,0,0,0,128,0,128,0,128,128,0,0,192,192,192,0,
+ 192,220,192,0,240,202,166,0,212,240,255,0,177,226,255,0,142,212,255,0,107,198,255,0,
+ 72,184,255,0,37,170,255,0,0,170,255,0,0,146,220,0,0,122,185,0,0,98,150,0,0,74,115,0,0,
+ 50,80,0,212,227,255,0,177,199,255,0,142,171,255,0,107,143,255,0,72,115,255,0,37,87,255,0,0,
+ 85,255,0,0,73,220,0,0,61,185,0,0,49,150,0,0,37,115,0,0,25,80,0,212,212,255,0,177,177,255,0,
+ 142,142,255,0,107,107,255,0,72,72,255,0,37,37,255,0,0,0,254,0,0,0,220,0,0,0,185,0,0,0,150,0,
+ 0,0,115,0,0,0,80,0,227,212,255,0,199,177,255,0,171,142,255,0,143,107,255,0,115,72,255,0,
+ 87,37,255,0,85,0,255,0,73,0,220,0,61,0,185,0,49,0,150,0,37,0,115,0,25,0,80,0,240,212,255,0,
+ 226,177,255,0,212,142,255,0,198,107,255,0,184,72,255,0,170,37,255,0,170,0,255,0,146,0,220,0,
+ 122,0,185,0,98,0,150,0,74,0,115,0,50,0,80,0,255,212,255,0,255,177,255,0,255,142,255,0,255,107,255,0,
+ 255,72,255,0,255,37,255,0,254,0,254,0,220,0,220,0,185,0,185,0,150,0,150,0,115,0,115,0,80,0,80,0,
+ 255,212,240,0,255,177,226,0,255,142,212,0,255,107,198,0,255,72,184,0,255,37,170,0,255,0,170,0,
+ 220,0,146,0,185,0,122,0,150,0,98,0,115,0,74,0,80,0,50,0,255,212,227,0,255,177,199,0,255,142,171,0,
+ 255,107,143,0,255,72,115,0,255,37,87,0,255,0,85,0,220,0,73,0,185,0,61,0,150,0,49,0,115,0,37,0,
+ 80,0,25,0,255,212,212,0,255,177,177,0,255,142,142,0,255,107,107,0,255,72,72,0,255,37,37,0,254,0,
+ 0,0,220,0,0,0,185,0,0,0,150,0,0,0,115,0,0,0,80,0,0,0,255,227,212,0,255,199,177,0,255,171,142,0,
+ 255,143,107,0,255,115,72,0,255,87,37,0,255,85,0,0,220,73,0,0,185,61,0,0,150,49,0,0,115,37,0,
+ 0,80,25,0,0,255,240,212,0,255,226,177,0,255,212,142,0,255,198,107,0,255,184,72,0,255,170,37,0,
+ 255,170,0,0,220,146,0,0,185,122,0,0,150,98,0,0,115,74,0,0,80,50,0,0,255,255,212,0,255,255,177,0,
+ 255,255,142,0,255,255,107,0,255,255,72,0,255,255,37,0,254,254,0,0,220,220,0,0,185,185,0,0,150,150,0,
+ 0,115,115,0,0,80,80,0,0,240,255,212,0,226,255,177,0,212,255,142,0,198,255,107,0,184,255,72,0,
+ 170,255,37,0,170,255,0,0,146,220,0,0,122,185,0,0,98,150,0,0,74,115,0,0,50,80,0,0,227,255,212,0,
+ 199,255,177,0,171,255,142,0,143,255,107,0,115,255,72,0,87,255,37,0,85,255,0,0,73,220,0,0,61,185,0,
+ 0,49,150,0,0,37,115,0,0,25,80,0,0,212,255,212,0,177,255,177,0,142,255,142,0,107,255,107,0,72,255,72,0,
+ 37,255,37,0,0,254,0,0,0,220,0,0,0,185,0,0,0,150,0,0,0,115,0,0,0,80,0,0,212,255,227,0,177,255,199,0,
+ 142,255,171,0,107,255,143,0,72,255,115,0,37,255,87,0,0,255,85,0,0,220,73,0,0,185,61,0,0,150,49,0,0,
+ 115,37,0,0,80,25,0,212,255,240,0,177,255,226,0,142,255,212,0,107,255,198,0,72,255,184,0,37,255,170,0,
+ 0,255,170,0,0,220,146,0,0,185,122,0,0,150,98,0,0,115,74,0,0,80,50,0,212,255,255,0,177,255,255,0,
+ 142,255,255,0,107,255,255,0,72,255,255,0,37,255,255,0,0,254,254,0,0,220,220,0,0,185,185,0,0,
+ 150,150,0,0,115,115,0,0,80,80,0,242,242,242,0,230,230,230,0,218,218,218,0,206,206,206,0,194,194,194,0,
+ 182,182,182,0,170,170,170,0,158,158,158,0,146,146,146,0,134,134,134,0,122,122,122,0,110,110,110,0,
+ 98,98,98,0,86,86,86,0,74,74,74,0,62,62,62,0,50,50,50,0,38,38,38,0,26,26,26,0,14,14,14,0,240,251,255,0,
+ 164,160,160,0,128,128,128,0,0,0,255,0,0,255,0,0,0,255,255,0,255,0,0,0,255,0,255,0,255,255,0,0,255,255,255,0};
+ memcpy(GetPalette(),pal256,1024);
+ break;
+ }
+ case 4:
+ {
+ const BYTE pal16[64]={0,0,0,0,0,0,128,0,0,128,0,0,0,128,128,0,128,0,0,0,128,0,128,0,128,128,0,0,192,192,192,0,
+ 128,128,128,0,0,0,255,0,0,255,0,0,0,255,255,0,255,0,0,0,255,0,255,0,255,255,0,0,255,255,255,0};
+ memcpy(GetPalette(),pal16,64);
+ break;
+ }
+ case 1:
+ {
+ const BYTE pal2[8]={0,0,0,0,255,255,255,0};
+ memcpy(GetPalette(),pal2,8);
+ break;
+ }
+ }
+ info.last_c_isvalid = false;
+ return;
+}
+////////////////////////////////////////////////////////////////////////////////
diff --git a/CxImage/ximapng.cpp b/CxImage/ximapng.cpp
new file mode 100644
index 00000000..b90a1aea
--- /dev/null
+++ b/CxImage/ximapng.cpp
@@ -0,0 +1,536 @@
+/*
+ * File: ximapng.cpp
+ * Purpose: Platform Independent PNG Image Class Loader and Writer
+ * 07/Aug/2001 Davide Pizzolato - www.xdp.it
+ * CxImage version 6.0.0 02/Feb/2008
+ */
+
+#include "ximapng.h"
+
+#if CXIMAGE_SUPPORT_PNG
+
+#include "ximaiter.h"
+
+////////////////////////////////////////////////////////////////////////////////
+void CxImagePNG::ima_png_error(png_struct *png_ptr, char *message)
+{
+ strcpy(info.szLastError,message);
+ longjmp(png_ptr->jmpbuf, 1);
+}
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+void CxImagePNG::expand2to4bpp(BYTE* prow)
+{
+ BYTE *psrc,*pdst;
+ BYTE pos,idx;
+ for(long x=head.biWidth-1;x>=0;x--){
+ psrc = prow + ((2*x)>>3);
+ pdst = prow + ((4*x)>>3);
+ pos = (BYTE)(2*(3-x%4));
+ idx = (BYTE)((*psrc & (0x03<>pos);
+ pos = (BYTE)(4*(1-x%2));
+ *pdst &= ~(0x0F<jmpbuf)) {
+ /* Free all of the memory associated with the png_ptr and info_ptr */
+ delete [] row_pointers;
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ cx_throw("");
+ }
+
+ // use custom I/O functions
+ png_set_read_fn(png_ptr, hFile, /*(png_rw_ptr)*/user_read_data);
+ png_set_error_fn(png_ptr,info.szLastError,/*(png_error_ptr)*/user_error_fn,NULL);
+
+ /* read the file information */
+ png_read_info(png_ptr, info_ptr);
+
+ if (info.nEscape == -1){
+ head.biWidth = info_ptr->width;
+ head.biHeight= info_ptr->height;
+ info.dwType = CXIMAGE_FORMAT_PNG;
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+
+ /* calculate new number of channels */
+ int channels=0;
+ switch(info_ptr->color_type){
+ case PNG_COLOR_TYPE_GRAY:
+ case PNG_COLOR_TYPE_PALETTE:
+ channels = 1;
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ channels = 2;
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ channels = 3;
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ channels = 4;
+ break;
+ default:
+ strcpy(info.szLastError,"unknown PNG color type");
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+
+ //find the right pixel depth used for cximage
+ int pixel_depth = info_ptr->pixel_depth;
+ if (channels == 1 && pixel_depth>8) pixel_depth=8;
+ if (channels == 2) pixel_depth=8;
+ if (channels >= 3) pixel_depth=24;
+
+ if (!Create(info_ptr->width, info_ptr->height, pixel_depth, CXIMAGE_FORMAT_PNG)){
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+
+ /* get metrics */
+ switch (info_ptr->phys_unit_type)
+ {
+ case PNG_RESOLUTION_UNKNOWN:
+ SetXDPI(info_ptr->x_pixels_per_unit);
+ SetYDPI(info_ptr->y_pixels_per_unit);
+ break;
+ case PNG_RESOLUTION_METER:
+ SetXDPI((long)floor(info_ptr->x_pixels_per_unit * 254.0 / 10000.0 + 0.5));
+ SetYDPI((long)floor(info_ptr->y_pixels_per_unit * 254.0 / 10000.0 + 0.5));
+ break;
+ }
+
+ if (info_ptr->num_palette>0){
+ SetPalette((rgb_color*)info_ptr->palette,info_ptr->num_palette);
+ SetClrImportant(info_ptr->num_palette);
+ } else if (info_ptr->bit_depth ==2) { // needed for 2 bpp grayscale PNGs
+ SetPaletteColor(0,0,0,0);
+ SetPaletteColor(1,85,85,85);
+ SetPaletteColor(2,170,170,170);
+ SetPaletteColor(3,255,255,255);
+ } else SetGrayPalette(); // needed for grayscale PNGs
+
+ int nshift = max(0,(info_ptr->bit_depth>>3)-1)<<3;
+
+ if (info_ptr->num_trans!=0){ //palette transparency
+ if (info_ptr->num_trans==1){
+ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE){
+ info.nBkgndIndex = info_ptr->trans_color.index;
+ } else{
+ info.nBkgndIndex = info_ptr->trans_color.gray>>nshift;
+ }
+ }
+ if (info_ptr->num_trans>1){
+ RGBQUAD* pal=GetPalette();
+ if (pal){
+ DWORD ip;
+ for (ip=0;ipnum_trans);ip++)
+ pal[ip].rgbReserved=info_ptr->trans_alpha[ip];
+ for (ip=info_ptr->num_trans;iptrans_color.red>>nshift);
+ info.nBkgndColor.rgbGreen = (BYTE)(info_ptr->trans_color.green>>nshift);
+ info.nBkgndColor.rgbBlue = (BYTE)(info_ptr->trans_color.blue>>nshift);
+ info.nBkgndColor.rgbReserved = 0;
+ info.nBkgndIndex = 0;
+ }
+ }
+
+ int alpha_present = (channels - 1) % 2;
+ if (alpha_present){
+#if CXIMAGE_SUPPORT_ALPHA //
+ AlphaCreate();
+#else
+ png_set_strip_alpha(png_ptr);
+#endif //CXIMAGE_SUPPORT_ALPHA
+ }
+
+ // - flip the RGB pixels to BGR (or RGBA to BGRA)
+ if (info_ptr->color_type & PNG_COLOR_MASK_COLOR){
+ png_set_bgr(png_ptr);
+ }
+
+ // - handle cancel
+ if (info.nEscape) longjmp(png_ptr->jmpbuf, 1);
+
+ // row_bytes is the width x number of channels x (bit-depth / 8)
+ row_pointers = new BYTE[info_ptr->rowbytes + 8];
+
+ // turn on interlace handling
+ int number_passes = png_set_interlace_handling(png_ptr);
+
+ if (number_passes>1){
+ SetCodecOption(1);
+ } else {
+ SetCodecOption(0);
+ }
+
+ int chan_offset = info_ptr->bit_depth >> 3;
+ int pixel_offset = info_ptr->pixel_depth >> 3;
+
+ for (int pass=0; pass < number_passes; pass++) {
+ iter.Upset();
+ int y=0;
+ do {
+
+ // - handle cancel
+ if (info.nEscape) longjmp(png_ptr->jmpbuf, 1);
+
+#if CXIMAGE_SUPPORT_ALPHA //
+ if (AlphaIsValid()) {
+
+ //compute the correct position of the line
+ long ax,ay;
+ ay = head.biHeight-1-y;
+ BYTE* prow= iter.GetRow(ay);
+
+ //recover data from previous scan
+ if (info_ptr->interlace_type && pass>0 && pass!=7){
+ for(ax=0;ax RGB + A
+ for(ax=0;axinterlace_type && pass>0){
+ iter.GetRow(row_pointers, info_ptr->rowbytes);
+ //re-expand buffer for images with bit depth > 8
+ if (info_ptr->bit_depth > 8){
+ for(long ax=(head.biWidth*channels-1);ax>=0;ax--)
+ row_pointers[ax*chan_offset] = row_pointers[ax];
+ }
+ }
+
+ //read next row
+ png_read_row(png_ptr, row_pointers, NULL);
+
+ //shrink 16 bit depth images down to 8 bits
+ if (info_ptr->bit_depth > 8){
+ for(long ax=0;ax<(head.biWidth*channels);ax++)
+ row_pointers[ax] = row_pointers[ax*chan_offset];
+ }
+
+ //copy the pixels
+ iter.SetRow(row_pointers, info_ptr->rowbytes);
+ // expand 2 bpp images only in the last pass
+ if (info_ptr->bit_depth==2 && pass==(number_passes-1))
+ expand2to4bpp(iter.GetRow());
+
+ //go on
+ iter.PrevRow();
+ }
+
+ y++;
+ } while(yjmpbuf)){
+ /* If we get here, we had a problem reading the file */
+ if (info_ptr->palette) free(info_ptr->palette);
+ png_destroy_write_struct(&png_ptr, (png_infopp)&info_ptr);
+ cx_throw("Error saving PNG file");
+ }
+
+ /* set up the output control */
+ //png_init_io(png_ptr, hFile);
+
+ // use custom I/O functions
+ png_set_write_fn(png_ptr,hFile,/*(png_rw_ptr)*/user_write_data,/*(png_flush_ptr)*/user_flush_data);
+
+ /* set the file information here */
+ info_ptr->width = GetWidth();
+ info_ptr->height = GetHeight();
+ info_ptr->pixel_depth = (BYTE)GetBpp();
+ info_ptr->channels = (GetBpp()>8) ? (BYTE)3: (BYTE)1;
+ info_ptr->bit_depth = (BYTE)(GetBpp()/info_ptr->channels);
+ info_ptr->compression_type = info_ptr->filter_type = 0;
+ info_ptr->valid = 0;
+
+ switch(GetCodecOption(CXIMAGE_FORMAT_PNG)){
+ case 1:
+ info_ptr->interlace_type = PNG_INTERLACE_ADAM7;
+ break;
+ default:
+ info_ptr->interlace_type = PNG_INTERLACE_NONE;
+ }
+
+ /* set compression level */
+ //png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
+
+ bool bGrayScale = IsGrayScale();
+
+ if (GetNumColors()){
+ if (bGrayScale){
+ info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
+ } else {
+ info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+ }
+ } else {
+ info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+ }
+#if CXIMAGE_SUPPORT_ALPHA
+ if (AlphaIsValid()){
+ info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+ info_ptr->channels++;
+ info_ptr->bit_depth = 8;
+ info_ptr->pixel_depth += 8;
+ }
+#endif
+
+ /* set background */
+ png_color_16 image_background={ 0, 255, 255, 255, 0 };
+ RGBQUAD tc = GetTransColor();
+ if (info.nBkgndIndex>=0) {
+ image_background.blue = tc.rgbBlue;
+ image_background.green = tc.rgbGreen;
+ image_background.red = tc.rgbRed;
+ }
+ png_set_bKGD(png_ptr, info_ptr, &image_background);
+
+ /* set metrics */
+ png_set_pHYs(png_ptr, info_ptr, head.biXPelsPerMeter, head.biYPelsPerMeter, PNG_RESOLUTION_METER);
+
+ png_set_IHDR(png_ptr, info_ptr, info_ptr->width, info_ptr->height, info_ptr->bit_depth,
+ info_ptr->color_type, info_ptr->interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ // simple transparency
+ if (info.nBkgndIndex >= 0){
+ info_ptr->num_trans = 1;
+ info_ptr->valid |= PNG_INFO_tRNS;
+ info_ptr->trans_alpha = trans;
+ info_ptr->trans_color.index = (BYTE)info.nBkgndIndex;
+ info_ptr->trans_color.red = tc.rgbRed;
+ info_ptr->trans_color.green = tc.rgbGreen;
+ info_ptr->trans_color.blue = tc.rgbBlue;
+ info_ptr->trans_color.gray = info_ptr->trans_color.index;
+
+ // the transparency indexes start from 0 for non grayscale palette
+ if (!bGrayScale && head.biClrUsed && info.nBkgndIndex)
+ SwapIndex(0,(BYTE)info.nBkgndIndex);
+ }
+
+ /* set the palette if there is one */
+ if (GetPalette()){
+ if (!bGrayScale){
+ info_ptr->valid |= PNG_INFO_PLTE;
+ }
+
+ int nc = GetClrImportant();
+ if (nc==0) nc = GetNumColors();
+
+ if (info.bAlphaPaletteEnabled){
+ for(WORD ip=0; ip