/* * wabprint.c * * Purpose: * Print Contacts * * Owner: * vikramm. * * History: * * Ported from Athena mailnews\mail\msgprint.cpp 10/30/96 * * Copyright (C) Microsoft Corp. 1993, 1994. */ #include <_apipch.h> // Function prototypes extern BOOL PrintDlg(LPPRINTDLG lppd); extern HRESULT PrintDlgEx(LPPRINTDLGEX lppdex); INT_PTR CALLBACK fnPrintDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); HRESULT HrCreatePrintCallbackObject(LPIAB lpIAB, LPWABPRINTDIALOGCALLBACK * lppWABPCO, DWORD dwSelectedStyle); void ReleaseWABPrintCallbackObject(LPWABPRINTDIALOGCALLBACK lpWABPCO); SCODE ScInitPrintInfo( PRINTINFO * ppi, HWND hwnd, LPTSTR szHeader, RECT * prcBorder, HWND hWndRE); int GetNumberFromStringResource(int idNumString); BOOL bCheckForPrintExtensions(LPTSTR lpDLLPath, DWORD cchSize); HRESULT HrUseWABPrintExtension(HWND hWnd, LPADRBOOK lpAdrBook, HWND hWndLV); // // Some string constants used in text formatting // const LPTSTR lpszTab = TEXT("\t"); const LPTSTR lpszFlatLine = TEXT("________________________________________________________________"); const LPTSTR lpszSpace = TEXT(" "); // // Print options ... // enum _PrintRange { rangeAll=0, rangeSelected }; enum _PrintStyles { styleMemo=0, styleBusinessCard, stylePhoneList }; static DWORD rgPrintHelpIDs[] = { IDC_PRINT_FRAME_STYLE, IDH_WAB_COMM_GROUPBOX, IDC_PRINT_RADIO_MEMO, IDH_WAB_PRINT_MEMO, IDC_PRINT_RADIO_CARD, IDH_WAB_PRINT_BIZCARD, IDC_PRINT_RADIO_PHONELIST, IDH_WAB_PRINT_PHONELIST, 0,0 }; // // This structure contains information about a specific contact // enum _MemoStrings { memoTitleName=0, // the big name that will be displayed based on the current sort settings .. memoName, memoJobTitle, memoDepartment, memoOffice, memoCompany, memoBusinessAddress, // Don't mess with the order of home and business address tags memoBusinessAddressStreet, memoBusinessAddressCity, memoBusinessAddressState, memoBusinessAddressZip, memoBusinessAddressCountry, memoHomeAddress, memoHomeAddressStreet, memoHomeAddressCity, memoHomeAddressState, memoHomeAddressZip, memoHomeAddressCountry, memoBusinessPhone, // Dont mess with the phone numbers - they should all be together in this order memoBusinessFax, memoBusinessPager, memoHomePhone, memoHomeFax, memoHomeCellular, memoEmail, memoBusinessWebPage, memoHomeWebPage, memoNotes, memoGroupMembers, memoMAX }; typedef struct _MemoInfo { LPTSTR lpszLabel[memoMAX]; LPTSTR lpsz[memoMAX]; } MEMOINFO, * LPMEMOINFO; TCHAR szDontDisplayInitials[16]; /* * c o n s t a n t s */ #define cTwipsPerInch 1440 #define cPtsPerInch 72 #ifndef WIN16 #define INT_MAX 2147483647 #endif #define cySepFontSize(_ppi) (12 * (_ppi)->sizeInch.cy / cPtsPerInch) #define CCHMAX_STRINGRES MAX_UI_STR /* * m a c r o s */ #define ScPrintRestOfPage(_ppi,_fAdvance) ScGetNextBand( (_ppi), (_fAdvance)) /* * g l o b a l s */ static TCHAR szDefFont[] = TEXT("Arial"); static TCHAR szThaiDefFont[] = TEXT("Cordia New"); static BOOL s_bUse20 = TRUE; // Default margin settings static RECT g_rcBorder = { cTwipsPerInch * 1 / 2, // distance from left cTwipsPerInch * 3 / 4, // distance from top cTwipsPerInch * 1 / 2, // distance from right cTwipsPerInch * 1 / 2 // distance from bottom }; /* * p r o t o t y p e s */ SCODE ScGetNextBand( PRINTINFO * ppi, BOOL fAdvance ); LONG LGetHeaderIndent(); //$$///////////////////////////////////////////////////////////////////////////// // // CleanPrintAddressString // // The Home and Business addresses are FormatMessaged and may contain redundant // spaces and line breaks if input data is incomplete // We strip out those spaces etc // ///////////////////////////////////////////////////////////////////////////////// void CleanPrintAddressString(LPTSTR szAddress) { LPTSTR lpTemp = szAddress; LPTSTR lpTemp2 = NULL; // The original template for styleMemo is // TEXT("%1\r\n\t%2 %3 %4\r\n\t%5") // // Worst case, we will get // TEXT("\r\n\t \r\n\t") // // We want to reduce double spaces to single space // We want to strip out empty line breaks // We want to strip out redundant tabs // // For style styleBusinessCard, there are no tabs and we // strip out redundancies accordingly // TrimSpaces(szAddress); // Squish multiple space blocks to a single space while (*lpTemp) { if (IsSpace(lpTemp) && IsSpace(CharNext(lpTemp))) { DWORD cchSize = lstrlen(lpTemp); // Boy I hate using this kind of logic, but it works here. // There are >= 2 spaces starting at lpTemp lpTemp2 = CharNext(lpTemp); // point to 2nd space StrCpyN(lpTemp, lpTemp2, cchSize); continue; // Cycle again with same lpTemp } lpTemp = CharNext(lpTemp); } TrimSpaces(szAddress); lpTemp = szAddress; // Dont let it start with a line break while(*lpTemp == '\r' && *(lpTemp+1) == '\n') { DWORD cchSize = lstrlen(lpTemp); // Boy I hate using this kind of logic, but it works here. lpTemp2 = lpTemp+2; if(*lpTemp2 == '\t') lpTemp2 = CharNext(lpTemp2); StrCpyN(lpTemp, lpTemp2, cchSize); TrimSpaces(lpTemp); } // Dont let it end with a line break if(lstrlen(szAddress)) { int nLen = lstrlen(szAddress); lpTemp = szAddress; while( (*(lpTemp + nLen - 3)=='\r' && *(lpTemp + nLen - 2)=='\n') || (*(lpTemp + nLen - 2)=='\r' && *(lpTemp + nLen - 1)=='\n') ) { if(*(lpTemp + nLen -3) == '\r') *(lpTemp + nLen - 3)='\0'; else *(lpTemp + nLen - 2)='\0'; TrimSpaces(szAddress); nLen = lstrlen(szAddress); lpTemp = szAddress; } } TrimSpaces(szAddress); return; } //$$//////////////////////////////////////////////////////////////////////// // // AddTabsToLineBreaks - For the memo format, our paragraph format for the // data on the right side gives each paragraph a default indentation // of 1 tab space after the first line. However if the data contains // line breaks, the paragraph format gets messed up. So we take // a data string and insert a tab after each line break. There are // only a few data values such as Address and Notes that need this // multi-line treatment. // //////////////////////////////////////////////////////////////////////////// void AddTabsToLineBreaks(LPTSTR * lppsz) { ULONG nBreaks = 0, nLen = 0; LPTSTR lpTemp,lpStart; LPTSTR lpsz; DWORD cchSize; if(!lppsz || !(*lppsz)) goto out; lpTemp = *lppsz; // count the number of breaks which are not followed by tabs while(*lpTemp) { if(*lpTemp == '\n' && *(lpTemp+1) != '\t') nBreaks++; lpTemp = CharNext(lpTemp); } if(!nBreaks) goto out; // Allocate a new string cchSize = (lstrlen(*lppsz)+1+nBreaks); lpsz = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize); if(!lpsz) goto out; lpTemp = *lppsz; lpStart = lpTemp; StrCpyN(lpsz, szEmpty, cchSize); // Copy over the old string into the new with appropriate breaks while(*lpTemp) { if((*lpTemp == '\n') && (*(lpTemp+1)!='\t')) { *lpTemp = '\0'; StrCatBuff(lpsz, lpStart, cchSize); StrCatBuff(lpsz, TEXT("\n"), cchSize); StrCatBuff(lpsz, lpszTab, cchSize); lpStart = lpTemp+1; lpTemp = lpStart; } else lpTemp = CharNext(lpTemp); } if(lstrlen(lpStart)) StrCatBuff(lpsz, lpStart, cchSize); LocalFreeAndNull(lppsz); *lppsz = lpsz; out: return; } //$$//////////////////////////////////////////////////////////////////////// // // FreeMemoInfoStruct - Frees the MemoInfo struct allocated strings // //////////////////////////////////////////////////////////////////////////// void FreeMemoInfoStruct(LPMEMOINFO lpMI) { int i; for(i=0;ilpsz[i] && (lpMI->lpsz[i] != szEmpty)) #ifdef WIN16 if(i == memoBusinessAddress || i == memoHomeAddress) FormatMessageFreeMem(lpMI->lpsz[i]); else #endif LocalFree(lpMI->lpsz[i]); if(lpMI->lpszLabel[i] && (lpMI->lpszLabel[i] != szEmpty)) LocalFree(lpMI->lpszLabel[i]); } } //$$//////////////////////////////////////////////////////////////////////////////////////////////// // // GetMemoInfoStruct - Parses the data in a PropArray and puts it into a Memo_Info struch along with // the propert labels, bsaed on the given style // // //////////////////////////////////////////////////////////////////////////////////////////////////// void GetMemoInfoStruct(LPADRBOOK lpAdrBook, ULONG ulcPropCount, LPSPropValue lpPropArray, DWORD dwStyle, LPMEMOINFO lpMI, BOOL bCurrentSortIsByLastName) { ULONG i,j; TCHAR szBuf[MAX_UI_STR]; LPTSTR lpszFirst = NULL; LPTSTR lpszMiddle = NULL; LPTSTR lpszLast = NULL; LPTSTR lpszDisplayName = NULL; LPTSTR lpszCompany = NULL; LPTSTR lpszNickName = NULL; BOOL bIsGroup = FALSE; int len = 0; if(!lpPropArray || !ulcPropCount) goto out; // special case initialization for(j=memoHomeAddressStreet;j<=memoHomeAddressCountry;j++) { lpMI->lpsz[j]=szEmpty; } for(j=memoBusinessAddressStreet;j<=memoBusinessAddressCountry;j++) { lpMI->lpsz[j]=szEmpty; } // Find out if this is a mailuser or a group for(i=0;ilpszLabel[nIndex] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(szBuf)+1)); if(!lpMI->lpszLabel[nIndex]) goto out; StrCpyN(lpMI->lpszLabel[nIndex], szBuf, lstrlen(szBuf)+1); } lpMI->lpsz[nIndex] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(lpPropArray[i].Value.LPSZ)+1)); if(!lpMI->lpsz[nIndex]) goto out; StrCpyN(lpMI->lpsz[nIndex], lpPropArray[i].Value.LPSZ, lstrlen(lpPropArray[i].Value.LPSZ)+1); } } // Email is a special case since a contact can hace PR_EMAIL_ADDRESS or // PR_CONTACT_EMAIL_ADDRESSES or both or neither // We first look for PR_CONTACT_EMAIL_ADDRESS .. if not found, then for // PR_EMAIL_ADDRESS { BOOL bMVEmail = FALSE; LPTSTR lpszEmails = NULL; for(i=0;i0) { StrCatBuff(lpszEmails, szCRLF,ulBufSize/sizeof(TCHAR) ); StrCatBuff(lpszEmails, lpszTab, ulBufSize/sizeof(TCHAR)); } StrCatBuff(lpszEmails,lpPropArray[i].Value.MVSZ.LPPSZ[k], ulBufSize/sizeof(TCHAR)); } bMVEmail = TRUE; break; } } if(!bMVEmail) { // No CONTACT_EMAIL_ADDRESSES // Should look for email address for(i=0;ilpsz[memoEmail] = lpszEmails; LoadString(hinstMapiX, idsPrintEmail, szBuf, ARRAYSIZE(szBuf)); lpMI->lpszLabel[memoEmail] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize); if(!lpMI->lpszLabel[memoEmail]) goto out; StrCpyN(lpMI->lpszLabel[memoEmail], szBuf, cchSize); } } //Now we have to format the Home and Business Addresses // { LPTSTR lpszData[5]; for(i=memoHomeAddressStreet;i<=memoHomeAddressCountry;i++) { // Win9x bug FormatMessage cannot have more than 1023 chars len += lstrlen(lpMI->lpsz[i]); if(len < 1023) lpszData[i-memoHomeAddressStreet] = lpMI->lpsz[i]; else lpszData[i-memoHomeAddressStreet] = NULL; } for(i=memoHomeAddressStreet;i<=memoHomeAddressCountry;i++) { if(lpMI->lpsz[i] && lpMI->lpsz[i] != szEmpty) { LPTSTR lpszHomeAddress = NULL; TCHAR szBuf[MAX_UI_STR]; int nStringID = (dwStyle == styleMemo) ? idsPrintAddressTemplate : idsPrintBusCardAddressTemplate ; LoadString(hinstMapiX, nStringID, szBuf, CharSizeOf(szBuf)); if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY, szBuf, 0, // stringid 0, // dwLanguageId (LPTSTR)&lpszHomeAddress, // output buffer 0, //MAX_UI_STR (va_list *)&lpszData[0])) { CleanPrintAddressString(lpszHomeAddress); lpMI->lpsz[memoHomeAddress] = lpszHomeAddress; szBuf[0]='\0'; LoadString(hinstMapiX, idsPrintHomeAddress, szBuf, CharSizeOf(szBuf)); lpMI->lpszLabel[memoHomeAddress] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(szBuf)+1)); if(!lpMI->lpszLabel[memoHomeAddress]) goto out; StrCpyN(lpMI->lpszLabel[memoHomeAddress], szBuf, lstrlen(szBuf)+1 ); break; } } } len = 0; for(i=memoBusinessAddressStreet;i<=memoBusinessAddressCountry;i++) { // Win9x bug FormatMessage cannot have more than 1023 chars len += lstrlen(lpMI->lpsz[i]); if(len < 1023) lpszData[i-memoBusinessAddressStreet] = lpMI->lpsz[i]; else lpszData[i-memoBusinessAddressStreet] = NULL; } for(i=memoBusinessAddressStreet;i<=memoBusinessAddressCountry;i++) { if(lpMI->lpsz[i] && lpMI->lpsz[i] != szEmpty) { LPTSTR lpszBusinessAddress = NULL; TCHAR szBuf[MAX_UI_STR]; int nStringID = (dwStyle == styleMemo) ? idsPrintAddressTemplate : idsPrintBusCardAddressTemplate ; TCHAR szTmp[MAX_PATH], *lpszTmp; LoadString(hinstMapiX, nStringID, szBuf, CharSizeOf(szBuf)); if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY, szBuf, 0, // stringid 0, // dwLanguageId (LPTSTR)&lpszBusinessAddress, // output buffer 0, //MAX_UI_STR (va_list *)&lpszData[0])) { CleanPrintAddressString(lpszBusinessAddress); lpMI->lpsz[memoBusinessAddress] = lpszBusinessAddress; szBuf[0]='\0'; LoadString(hinstMapiX, idsPrintBusinessAddress, szBuf, CharSizeOf(szBuf)); lpMI->lpszLabel[memoBusinessAddress] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(szBuf)+1)); if(!lpMI->lpszLabel[memoBusinessAddress]) goto out; StrCpyN(lpMI->lpszLabel[memoBusinessAddress], szBuf, lstrlen(szBuf)+1); break; } } } } // Set the name that will be printed out for each entry // This is dependent on the current view and on the local language setting { LPTSTR lpszTmp = NULL; if( bCurrentSortIsByLastName != bDNisByLN) { // for auto add to WABs we dont have all this info .. so // if we just have a displayname we use it as it is if(lpszFirst || lpszMiddle || lpszLast || lpszNickName || (lpszCompany && !lpszDisplayName)) { if(SetLocalizedDisplayName(lpszFirst, lpszMiddle, lpszLast, lpszCompany, lpszNickName, NULL, //&szBuf, 0, bCurrentSortIsByLastName, NULL, &lpszTmp)) { lpMI->lpsz[memoTitleName]=lpszTmp; } } } if(!lpMI->lpsz[memoTitleName]) { // use whatever DisplayName we have lpMI->lpsz[memoTitleName] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(lpszDisplayName)+1)); if(!lpMI->lpsz[memoTitleName]) goto out; StrCpyN(lpMI->lpsz[memoTitleName],lpszDisplayName, lstrlen(lpszDisplayName)+1); } } if(bIsGroup) { LPTSTR lpszMembers = NULL; ULONG nLen = 0; // Get the group members for(i=0;ilpszLabel[memoGroupMembers] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize); if(!lpMI->lpszLabel[memoGroupMembers]) goto out; StrCpyN(lpMI->lpszLabel[memoGroupMembers], szBuf, cchSize); lpMI->lpsz[memoGroupMembers]=lpszMembers; } } //Speacial case formatting of multiline data if(dwStyle == styleMemo) { AddTabsToLineBreaks(&(lpMI->lpsz[memoNotes])); AddTabsToLineBreaks(&(lpMI->lpsz[memoHomeAddress])); AddTabsToLineBreaks(&(lpMI->lpsz[memoBusinessAddress])); } out: // special case uninitialization for(j=memoHomeAddressStreet;j<=memoHomeAddressCountry;j++) { if(lpMI->lpsz[j] && (lpMI->lpsz[j] != szEmpty)) LocalFreeAndNull(&(lpMI->lpsz[j])); } for(j=memoBusinessAddressStreet;j<=memoBusinessAddressCountry;j++) { if(lpMI->lpsz[j] && (lpMI->lpsz[j] != szEmpty)) LocalFreeAndNull(&(lpMI->lpsz[j])); } return; } /* * NTwipsToPixels * * Purpose: * Converts a measurement in twips into pixels * * Arguments: * nTwips the value to be converted * cPixels number of pixels per inch * * Returns: * Returns a int representing the number of pixels in nTwips */ int NTwipsToPixels(int nTwips, int cPixelsPerInch) { LONG lT = (LONG) nTwips * (LONG) cPixelsPerInch / (LONG) cTwipsPerInch; return (int) lT; } /* * LPixelsToTwips * * Purpose: * Converts a measurement in pixles into twips * * Arguments: * nPixels the value to be converted * cPixels number of pixels per inch * * Returns: * Returns a int representing the number of pixels in nTwips */ LONG LPixelsToTwips(int nPixels, int cPixelsPerInch) { LONG lT = (LONG) nPixels * (LONG) cTwipsPerInch / (LONG) cPixelsPerInch; return lT; } /* * PrintPageNumber * * Purpose: * To print the page number for each page * * Arguments: * ppi Pointer to the PRINTINFO structure * * Returns: * SCODE indicating success or failure. * Currently always return S_OK */ void PrintPageNumber(PRINTINFO * ppi) { RECT rcExt; HFONT hfontOld; TCHAR szT[20]; DebugPrintTrace(( TEXT("PrintPageNumber\n"))); // Find out how much space our text take will take rcExt = ppi->rcBand; rcExt.top = ppi->yFooter; hfontOld = (HFONT)SelectObject(ppi->hdcPrn, ppi->hfontPlain); DrawText(ppi->hdcPrn, szT, wnsprintf(szT, ARRAYSIZE(szT), ppi->szPageNumber, ppi->lPageNumber), &rcExt, DT_CENTER); SelectObject(ppi->hdcPrn, hfontOld); } /* * ScGetNextBand * * Purpose: * Retrieves the next band to print on. Adjusts the band to conform * to the margins established in the PRINTINFO structure. Bumps up * the page number as appropriate. * * Arguments: * ppi print information * fAdvance flag whether to move to the next page * * Returns: * SCODE indicating the success or failure */ SCODE ScGetNextBand(PRINTINFO * ppi, BOOL fAdvance) { SCODE sc = S_OK; int nCode; DebugPrintTrace(( TEXT("ScGetNextBand\n"))); // Call the abort proc to see if the user wishes to stop if (!ppi->pfnAbortProc(ppi->hdcPrn, 0)) { sc=E_FAIL; nCode = AbortDoc(ppi->hdcPrn); if(nCode < 0) { DebugPrintTrace(( TEXT("Abort Doc error: %d\n"),GetLastError())); ShowMessageBox(ppi->hwndDlg, idsPrintJobCannotStop, MB_OK | MB_ICONEXCLAMATION); } goto CleanUp; } // brettm: // USE_BANDING stuff removed, as we're always on Win32 // End the previous page if (ppi->lPageNumber) { nCode = EndPage(ppi->hdcPrn); DebugPrintTrace(( TEXT("+++++++++EndPage\n"))); if (nCode <= 0) { sc=E_FAIL; goto CleanUp; } } if (fAdvance) { nCode = StartPage(ppi->hdcPrn); DebugPrintTrace(( TEXT("+++++++++StartPage\n"))); // Start a new page if (nCode <= 0) { sc=E_FAIL; goto CleanUp; } // Let the entire page be the band ppi->rcBand = ppi->rcMargin; ppi->fEndOfPage = TRUE; // end of page! // Bump up the page number and print ppi->lPrevPage = ppi->lPageNumber++; PrintPageNumber(ppi); { TCHAR szBuf[MAX_UI_STR]; TCHAR szString[MAX_UI_STR]; LoadString(hinstMapiX, idsPrintingPageNumber, szString, ARRAYSIZE(szString)); wnsprintf(szBuf, ARRAYSIZE(szBuf), szString, ppi->lPageNumber); SetPrintDialogMsg(0, 0, szBuf); } } CleanUp: return sc; } /* * LGetHeaderIndent * * Purpose: * Retrieves from the resource file the suggested indent overhang for * headers. * * Arguments: * none. * * Returns: * LONG The suggested indent overhang in twips */ LONG LGetHeaderIndent() { LONG lOver = 1440; // default //TCHAR szT[10]; //if (LoadString(hinstMapiX, idsHeaderIndent, szT, CharSizeOf(szT))) // lOver = atoi(szT); return lOver; } //$$//////////////////////////////////////////////////////////////////////////////// // // AppendText - Simple routine that appends a given string to the End of the text // in the given richedit control // //////////////////////////////////////////////////////////////////////////////////// void AppendText(HWND hWndRE, LPTSTR lpsz) { // Set the insertion point to the end of the current text int nLastChar = (int) SendMessage(hWndRE, WM_GETTEXTLENGTH, 0, 0); CHARRANGE charRange = {0}; charRange.cpMin = charRange.cpMax = nLastChar + 1; SendMessage(hWndRE, EM_EXSETSEL, 0, (LPARAM) &charRange); // Insert the text // [PaulHi] 7/7/99 Raid 82350 RichEdit 1.0 can't handle Unicode // strings even though the window is created Unicode. if (s_bUse20) { // RichEdit 2.0 SendMessage(hWndRE, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) lpsz); } else { // RichEdit 1.0 LPSTR lpszTemp = ConvertWtoA(lpsz); Assert(lpszTemp); if (lpszTemp) SendMessageA(hWndRE, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) lpszTemp); LocalFreeAndNull(&lpszTemp); } return; } //$$//////////////////////////////////////////////////////////////////////// // // ParaCmd - Sets/Unsets paragraph formatting in the Rich Edit Control // // We want all the information on the right side to be indented // so we will put an indent on that information and remove it when // adding labels //////////////////////////////////////////////////////////////////////////// void ParaCmd(HWND hWndRE, BOOL bIndent) { // We want no indentation on the first line and we want a // 1 tab indentation on the second line onwards PARAFORMAT pf ={0}; int nTabStop = (int) (1.5 * cTwipsPerInch); pf.cbSize = sizeof(pf); pf.dwMask = PFM_OFFSET | PFM_TABSTOPS | PFM_NUMBERING; SendMessage(hWndRE, EM_GETPARAFORMAT, (WPARAM) TRUE, (LPARAM) &pf); pf.wNumbering = 0; if (bIndent) { //pf.dxStartIndent = nTabStop; pf.dxOffset = nTabStop; pf.cTabCount = 1; pf.rgxTabs[0] = nTabStop; } else { //pf.dxStartIndent = 0; pf.dxOffset = 0; pf.cTabCount = 1; pf.rgxTabs[0] = 720; //seems to be the default = 0.5 inches } SendMessage(hWndRE, EM_SETPARAFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &pf); return; } //$$//////////////////////////////////////////////////////////////////////// // // BoldCmd - Sets/Unsets current font to bold in the Rich Edit Control // //////////////////////////////////////////////////////////////////////////// void BoldCmd(HWND hWndRE, BOOL bBold) { CHARFORMAT cf = {0}; cf.cbSize = sizeof(cf); cf.dwMask = CFM_BOLD; SendMessage(hWndRE, EM_GETCHARFORMAT, (WPARAM) TRUE, (LPARAM) &cf); if (bBold) cf.dwEffects = cf.dwEffects | CFE_BOLD; else cf.dwEffects = cf.dwEffects & ~CFE_BOLD; SendMessage(hWndRE, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf); return; } //$$//////////////////////////////////////////////////////////////////////// // // TitleCmd - Sets/Unsets title text (BOLD, Bigger) in the Rich Edit Control // //////////////////////////////////////////////////////////////////////////// void TitleCmd(HWND hWndRE, BOOL bBold) { CHARFORMAT cf = {0}; PARAFORMAT pf = {0}; cf.cbSize = sizeof(cf); cf.dwMask = CFM_BOLD /*| CFM_ITALIC*/ | CFM_SIZE; pf.cbSize = sizeof(pf); pf.dwMask = PFM_NUMBERING; SendMessage(hWndRE, EM_GETPARAFORMAT, (WPARAM) TRUE, (LPARAM) &pf); SendMessage(hWndRE, EM_GETCHARFORMAT, (WPARAM) TRUE, (LPARAM) &cf); if (bBold) { cf.dwEffects = cf.dwEffects | CFE_BOLD; // | CFE_ITALIC; cf.yHeight += 50; pf.wNumbering = PFN_BULLET; } else { cf.dwEffects = cf.dwEffects & ~CFE_BOLD; // cf.dwEffects = cf.dwEffects & ~CFE_ITALIC; cf.yHeight -= 50; pf.wNumbering = 0; } SendMessage(hWndRE, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf); SendMessage(hWndRE, EM_SETPARAFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &pf); return; } //$$//////////////////////////////////////////////////////////////////////// // // ReduceFontCmd - Reduces the displayed font in the Rich Edit Control // //////////////////////////////////////////////////////////////////////////// void ReduceFontCmd(HWND hWndRE, BOOL bReduce, int nReduceBy, BOOL bSelectionOnly) { CHARFORMAT cf = {0}; cf.cbSize = sizeof(cf); cf.dwMask = CFM_SIZE; SendMessage(hWndRE, EM_GETCHARFORMAT, (WPARAM) bSelectionOnly, (LPARAM) &cf); if (bReduce) cf.yHeight -= nReduceBy; //40; else cf.yHeight += nReduceBy; //40; SendMessage(hWndRE, EM_SETCHARFORMAT, (WPARAM) bSelectionOnly ? SCF_SELECTION : SCF_DEFAULT, (LPARAM) &cf); return; } //$$//////////////////////////////////////////////////////////////////////// // // SetTabsCmd - Sets and Unsets the Tabs in the RichEdit Control // //////////////////////////////////////////////////////////////////////////// void SetTabsCmd(HWND hWndRE, BOOL bSet) { PARAFORMAT pf ={0}; int nTabStop = (int) (1.5 * cTwipsPerInch); int j; pf.cbSize = sizeof(pf); pf.dwMask = PFM_TABSTOPS | PFM_NUMBERING; SendMessage(hWndRE, EM_GETPARAFORMAT, (WPARAM) TRUE, (LPARAM) &pf); pf.wNumbering = 0; if (bSet) { for(j=0;j<5;j++) pf.rgxTabs[j] = nTabStop; } else { for(j=0;j<5;j++) pf.rgxTabs[j] = 720; } SendMessage(hWndRE, EM_SETPARAFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &pf); return; } //$$//////////////////////////////////////////////////////////////////////////////////////////// // // WABStylePhoneList - Fills the Rich edit control with info from MI as per the // Phone List style // // hWndRE - handle to Print Formating Rich Edit Control // MI - MEMOINFO strcuture containing the info to print // lpszPrevEntry - the first TCHAR of the previous entry - this lets us break the list // alphabetically - this points to a preallocated buffer // //////////////////////////////////////////////////////////////////////////////////////////////// void WABStylePhoneList(HWND hWndRE, MEMOINFO MI, LPTSTR lpszPrevEntry, DWORD cchSizePrevEntry) { // We want an extra gap between certain groups of information // we'll track these groups using these BOOLs ULONG i,j,k; TCHAR szBufChar1[16]; TCHAR szBufChar2[16]; LPTSTR lpTemp = NULL; int nReduceFontBy = GetNumberFromStringResource(idsPhoneFontReduceBy); // First we compare the first character of the current string with the previous // string - if it is the same, then we do nothing - if it different, we ouput // the lower case TCHAR as a heading for the phone directory // // If the character is not alphanumeric, we ignore it as a heading (e.g. ' ) // Bug: 25710 // We ignore these initialls totally if localizers have set idsDontDisplayInitials // these initials to anything other than 0 because in some FE languages // names have double characters in them and they look strange with a single // character up front if(szDontDisplayInitials[0] == '0') { StrCpyN(szBufChar1, lpszPrevEntry, ARRAYSIZE(szBufChar1)); if(lstrlen(MI.lpsz[memoTitleName]) > 16) { ULONG iLen = TruncatePos(MI.lpsz[memoTitleName], 16-1); CopyMemory(szBufChar2, MI.lpsz[memoTitleName], sizeof(TCHAR)*iLen); szBufChar2[iLen]='\0'; } else StrCpyN(szBufChar2, MI.lpsz[memoTitleName], ARRAYSIZE(szBufChar2)); /*********** // Bug 14615 - this alphanumeric filtering doesnt work for DBCS and FE names // // Ignore all non-alpha numeric characters lpTemp = szBufChar2; { //Temp Hack TCHAR szTemp[16]; LPTSTR lpTemp2 = NULL; StrCpyN(szTemp, lpTemp, ARRAYSIZE(szTemp)); lpTemp2 = CharNext(szTemp); *lpTemp2 = '\0'; while(lpTemp && lstrlen(lpTemp)) { if(IsCharAlphaNumeric(szTemp[0])) break; lpTemp = CharNext(lpTemp); StrCpyN(szTemp, lpTemp, ARRAYSIZE(szTemp)); lpTemp2 = CharNext(szTemp); *lpTemp2 = '\0'; } } if(lpTemp != szBufChar2) StrCpyN(szBufChar2, lpTemp, ARRAYSIZE(szBufChar2)); ***************/ // Isolate the first TCHAR of the above strings lpTemp = CharNext(szBufChar1); *lpTemp = '\0'; lpTemp = CharNext(szBufChar2); *lpTemp = '\0'; // Compare these two characters CharLower(szBufChar1); CharLower(szBufChar2); if(lstrcmp(szBufChar1, szBufChar2)) { // They are different // Add the TCHAR as a title string AppendText(hWndRE, szCRLF); TitleCmd(hWndRE, TRUE); BoldCmd(hWndRE, TRUE); AppendText(hWndRE, lpszSpace); AppendText(hWndRE, szBufChar2); AppendText(hWndRE, szCRLF); TitleCmd(hWndRE, FALSE); BoldCmd(hWndRE, FALSE); ParaCmd(hWndRE, TRUE); AppendText(hWndRE, lpszFlatLine); AppendText(hWndRE, szCRLF); AppendText(hWndRE, szCRLF); ParaCmd(hWndRE, FALSE); StrCpyN(lpszPrevEntry, szBufChar2, cchSizePrevEntry); } } //dontdisplayinitials ReduceFontCmd(hWndRE, TRUE, nReduceFontBy, TRUE); SetTabsCmd(hWndRE, TRUE); // Figure out how much space the name will take up ... { TCHAR szBuf[MAX_PATH]; int nMaxTabs = 2; int nTabStop = (int)(1.5 * cTwipsPerInch); int MaxWidth = nMaxTabs * nTabStop; int sizeCxTwips; int PixelsPerInch; int nLen = lstrlen(MI.lpsz[memoTitleName]); SIZE size = {0}; HDC hdc = GetDC(hWndRE); { HDC hDC = GetDC(NULL); PixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSX); ReleaseDC(NULL, hDC); } if (nLen >= MAX_PATH) { ULONG iLen = TruncatePos(MI.lpsz[memoTitleName], MAX_PATH-1); CopyMemory(szBuf, MI.lpsz[memoTitleName], sizeof(TCHAR)*iLen); szBuf[iLen]='\0'; } else StrCpyN(szBuf, MI.lpsz[memoTitleName], ARRAYSIZE(szBuf)); nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); // We dont want our displayed name to be more than 2 tabstops // so we decide where to truncate the name to fit it on screen if(sizeCxTwips > MaxWidth) { while(sizeCxTwips > MaxWidth) { nLen--; nLen = TruncatePos(szBuf, nLen); szBuf[nLen]='\0'; nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); } // chop of 3 more characters for good measure nLen-=3; nLen = TruncatePos(szBuf, nLen); szBuf[nLen]='\0'; nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); } while ((sizeCxTwips < MaxWidth) && (nLen < ARRAYSIZE(szBuf)-1)) { StrCatBuff(szBuf, TEXT("."), ARRAYSIZE(szBuf)); nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); } StrCatBuff(szBuf, lpszTab, ARRAYSIZE(szBuf)); AppendText(hWndRE, szBuf); // Now we are ready to tag on the phone numbers { int nPhoneCount = 0; //counts how many phones there are int nPhoneLabelSpaceTwips = GetNumberFromStringResource(idsPhoneTextSpaceTwips); //1150 for(j=memoBusinessPhone;j<=memoHomeCellular;j++) { if(MI.lpsz[j] && lstrlen(MI.lpsz[j])) { if(nPhoneCount != 0) { int k; AppendText(hWndRE, szCRLF); // Bug 73266 if(s_bUse20) ReduceFontCmd(hWndRE, TRUE, nReduceFontBy, TRUE); StrCpyN(szBuf, szEmpty, ARRAYSIZE(szBuf)); nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); while ((sizeCxTwips < MaxWidth) && (nLen < ARRAYSIZE(szBuf)-1)) { StrCatBuff(szBuf, lpszSpace, ARRAYSIZE(szBuf)); nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); } StrCatBuff(szBuf, lpszTab, ARRAYSIZE(szBuf)); AppendText(hWndRE, szBuf); } TrimSpaces(MI.lpszLabel[j]); StrCpyN(szBuf, MI.lpszLabel[j], ARRAYSIZE(szBuf)); nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); if(sizeCxTwips < nPhoneLabelSpaceTwips) { while ((sizeCxTwips < nPhoneLabelSpaceTwips) && (nLen < ARRAYSIZE(szBuf)-1)) { StrCatBuff(szBuf, lpszSpace, ARRAYSIZE(szBuf)); nLen = lstrlen(szBuf); GetTextExtentPoint32(hdc, szBuf, nLen, &size); sizeCxTwips = (int)((size.cx * cTwipsPerInch)/PixelsPerInch); } StrCatBuff(szBuf, lpszTab, ARRAYSIZE(szBuf)); } StrCatBuff(szBuf, MI.lpsz[j], ARRAYSIZE(szBuf)); AppendText(hWndRE, szBuf); nPhoneCount++; } } if(nPhoneCount == 0) { LoadString(hinstMapiX, idsPrintNoPhone, szBuf, ARRAYSIZE(szBuf)); AppendText(hWndRE, szBuf); } } AppendText(hWndRE, szCRLF); ReleaseDC(hWndRE, hdc); } SetTabsCmd(hWndRE, FALSE); ReduceFontCmd(hWndRE, FALSE, nReduceFontBy, TRUE); return; } //$$//////////////////////////////////////////////////////////////////////////////////////////// // // WABStyleBusinessCard - Fills the Rich edit control with info from MI as per the // business card style // // hWndRE - handle to Print Formating Rich Edit Control // MI - MEMOINFO strcuture containing the info to print // //////////////////////////////////////////////////////////////////////////////////////////////// void WABStyleBusinessCard(HWND hWndRE, MEMOINFO MI) { // We want an extra gap between certain groups of information // we'll track these groups using these BOOLs ULONG i,j,k; int nReduceBy = GetNumberFromStringResource(idsBusCardFontReduceBy); // Add the contact name as a heading //TitleCmd(hWndRE, TRUE); BoldCmd(hWndRE, TRUE); //AppendText(hWndRE, lpszSpace); AppendText(hWndRE, MI.lpsz[memoTitleName]); AppendText(hWndRE, szCRLF); //TitleCmd(hWndRE, FALSE); BoldCmd(hWndRE, FALSE); ParaCmd(hWndRE, TRUE); AppendText(hWndRE, lpszFlatLine); AppendText(hWndRE, szCRLF); AppendText(hWndRE, szCRLF); ParaCmd(hWndRE, FALSE); ReduceFontCmd(hWndRE, TRUE, nReduceBy, TRUE); for(j=memoName;jcbEntryID, lpItem->lpEntryID, MAPI_UNICODE, &ulcPropCount, &lpPropArray) ) ) { DebugPrintError(( TEXT("HrGetPropArray failed\n"))); goto out; } GetMemoInfoStruct(lpIAB, ulcPropCount, lpPropArray, dwStyle, &MI, bCurrentSortIsByLastName); SetPrintDialogMsg(0, idsPrintFormattingName, MI.lpsz[memoTitleName]); // Poll the cancel dialog to see if the user wants to cancel if (!ppi->pfnAbortProc(ppi->hdcPrn, 0)) { FreeMemoInfoStruct(&MI); DebugPrintError(( TEXT("User canceled printing ...\n"))); goto out; } switch(dwStyle) { case styleMemo: WABStyleMemo(hWndRE, MI); break; case styleBusinessCard: WABStyleBusinessCard(hWndRE, MI); break; case stylePhoneList: WABStylePhoneList(hWndRE, MI, szPrevEntry, ARRAYSIZE(szPrevEntry)); //if(lpszPrevEntry) // LocalFreeAndNull(&lpszPrevEntry); //lpszPrevEntry = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(MI.lpsz[memoTitleName])+1)); //if(!lpszPrevEntry) //goto out; //lstrcpy(lpszPrevEntry, MI.lpsz[memoTitleName]); break; } FreeMemoInfoStruct(&MI); } if(lpPropArray) MAPIFreeBuffer(lpPropArray); lpPropArray = NULL; // fill in some space between multiple contacts { int numBreaks = (dwStyle == stylePhoneList) ? 1 : 4; for(j=0;jhwndRE; int ifrm; LONG lTextLength = 0; LONG lTextPrinted = 0; DebugPrintTrace(( TEXT("ScPrintBody\n"))); // Put a gap between the fields the message text rcSep = ppi->rcBand; if (rcSep.top + cyGap > ppi->yFooter) { // Adding the gap will go past the page. Just go to the next page sc = ScGetNextBand(ppi, TRUE); } else { // Keep on getting a band till the bottom of the band passes the gap while (rcSep.top + cyGap > ppi->rcBand.bottom) if ((sc = ScGetNextBand(ppi, TRUE)) != S_OK) goto CleanUp; // Adjust the band so that we don't damage the gap ppi->rcBand.top += cyGap + 1; } #ifdef DEBUG_PRINTMSGS InvalidateRect(ppi->hwndRE, NULL, TRUE); UpdateWindow(ppi->hwndRE); #endif // Format the text for printing fr.hdc = ppi->hdcPrn; fr.hdcTarget = 0; fr.rcPage.left = fr.rcPage.top = 0; fr.rcPage.right = (int)LPixelsToTwips(ppi->sizePage.cx, ppi->sizeInch.cx); fr.rcPage.bottom = (int)LPixelsToTwips(ppi->sizePage.cy, ppi->sizeInch.cy); fr.chrg.cpMin = 0; fr.chrg.cpMax = -1; lTextLength = (LONG) SendMessage(hwndRE, WM_GETTEXTLENGTH, 0, 0); lTextPrinted = 0; // Handle no body case if (lTextLength <= 0) goto CleanUp; // tell RichEdit not to erase the background before rendering text SetBkMode(fr.hdc, TRANSPARENT); do { fr.chrg.cpMin = lTextPrinted; // Tell format range where to render to fr.rc.top = (int) LPixelsToTwips(ppi->rcBand.top, ppi->sizeInch.cy); fr.rc.left = (int) LPixelsToTwips(ppi->rcBand.left, ppi->sizeInch.cx); fr.rc.right = (int) LPixelsToTwips(ppi->rcBand.right, ppi->sizeInch.cx); fr.rc.bottom = (int) LPixelsToTwips(min(ppi->rcBand.bottom, ppi->yFooter), ppi->sizeInch.cy); // Go draw it DebugPrintTrace(( TEXT("Rendering\r\n"))); lTextPrinted = (LONG) SendMessage(hwndRE, EM_FORMATRANGE, TRUE,(LPARAM) &fr); //TextOut(ppi->hdcPrn, fr.rc.left, fr.rc.top, lpszTxt, lstrlen(lpszTxt)); // weird bug with RichEdit20 .. lTextPrinted is actually reduces in size // Another weird bug ... lTextPrinted is never incremented. [PaulHi] if(lTextPrinted <= fr.chrg.cpMin) break; } while (lTextPrinted > 0 && lTextPrinted < lTextLength && (sc = ScGetNextBand(ppi, TRUE)) == S_OK); //$ Raid 1137: Need to clear out the cached font characteristics fr.chrg.cpMin = fr.chrg.cpMax + 1; //SendMessage(hwndRE, EM_FORMATRANGE, 0, 0); // Don't damage what we have just printed ppi->rcBand.top = NTwipsToPixels(fr.rc.bottom, ppi->sizeInch.cy); CleanUp: DebugPrintTrace(( TEXT("ScPrintBody:%d\n"), sc)); return sc; } /* * ScPrintMessage * * Purpose: * To print the header and body of a message * * Arguments: * ppi Pointer to the PRINTINFO structure * pmsg Pointer to the message which needs its header * to be printed. * hwndRE Pre-rendered body * phi Message header info * * Returns: * SCODE indicating success or failure * */ SCODE ScPrintMessage(PRINTINFO * ppi, HWND hWndRE) { RECT rcExt; RECT rcSep; HFONT hfontOld = NULL; HBRUSH hbrushOld = NULL; HPEN hpenOld = NULL; SIZE sizeExt; int cyHeader; SCODE sc = S_OK; PARAFORMAT pf = { 0 }; pf.cbSize = sizeof(PARAFORMAT); // If we currently have no band, get the next band if (IsRectEmpty(&ppi->rcBand) && (sc = ScGetNextBand(ppi, TRUE)) != S_OK) goto CleanUp; // Determine how much room we need for the header string and separator hfontOld = (HFONT)SelectObject(ppi->hdcPrn, ppi->hfontSep); hbrushOld = (HBRUSH)SelectObject(ppi->hdcPrn, GetStockObject(BLACK_BRUSH)); hpenOld = (HPEN)SelectObject(ppi->hdcPrn, GetStockObject(BLACK_PEN)); // Find out how much space our text take will take GetTextExtentPoint(ppi->hdcPrn, ppi->szHeader, lstrlen(ppi->szHeader), &sizeExt); cyHeader = 2 * sizeExt.cy + 1 + (cySepFontSize(ppi) / 4); // Check if enough room on page. Move to the next page as needed if (ppi->rcBand.top + cyHeader > ppi->yFooter) { // No more room on this page, see if it'll fit on the next page if (ppi->rcMargin.top + cyHeader > ppi->yFooter) { DebugPrintTrace(( TEXT("Header too big for any page.\n"))); goto CleanUp; } // Go on to the next page if ((sc = ScPrintRestOfPage(ppi, TRUE)) != S_OK) goto CleanUp; } // Calculate the rectangle that our header will take up rcExt = ppi->rcBand; rcExt.bottom = rcExt.top + cyHeader; rcSep = rcExt; rcSep.top += sizeExt.cy; rcSep.bottom = rcSep.top + (cySepFontSize(ppi) / 4); rcSep.right = rcSep.left + sizeExt.cx; // Draw the text and separator TextOut(ppi->hdcPrn, rcExt.left, rcExt.top, ppi->szHeader, lstrlen(ppi->szHeader)); Rectangle(ppi->hdcPrn, rcSep.left, rcSep.top, rcSep.right, rcSep.bottom); MoveToEx(ppi->hdcPrn, rcSep.right, rcSep.top, NULL); LineTo(ppi->hdcPrn, rcExt.right, rcSep.top); rcExt.top = rcExt.bottom + 5; /***/ // Adjust the band so that we don't damage the header ppi->rcBand.top = rcExt.bottom + 1; // Create a header in a richedit control pf.dwMask = PFM_STARTINDENT | PFM_RIGHTINDENT | PFM_ALIGNMENT | PFM_OFFSET | PFM_TABSTOPS; pf.dxOffset = LGetHeaderIndent(); pf.cTabCount = 1; pf.rgxTabs[0] = pf.dxOffset; pf.wAlignment = PFA_LEFT; sc = ScPrintBody(ppi, sizeExt.cy); /***/ CleanUp: if (hfontOld != NULL) SelectObject(ppi->hdcPrn, hfontOld); if (hbrushOld != NULL) SelectObject(ppi->hdcPrn, hbrushOld); if (hpenOld != NULL) SelectObject(ppi->hdcPrn, hpenOld); return sc; } #ifdef WIN16 typedef UINT (CALLBACK *LPPRINTHOOKPROC) (HWND, UINT, WPARAM, LPARAM); typedef UINT (CALLBACK *LPSETUPHOOKPROC) (HWND, UINT, WPARAM, LPARAM); #endif #ifdef WIN16 typedef UINT (CALLBACK *LPPRINTHOOKPROC) (HWND, UINT, WPARAM, LPARAM); typedef UINT (CALLBACK *LPSETUPHOOKPROC) (HWND, UINT, WPARAM, LPARAM); #endif //$$//////////////////////////////////////////////////////////////////////////////////////////// // // // SetPrintDlgExStruct - Fills in the default PDEX values // // hWnd - HWND of parent dialog // PD - PrintDLG struct // hWndLV - HWND of listView to print from - if there are no selections in the list view, // the selections option is turned off in the print dialog // //////////////////////////////////////////////////////////////////////////////////////////////// void SetPrintDlgExStruct(HWND hWnd, PRINTDLGEX * lpPD, HWND hWndLV, LPWABPRINTDIALOGCALLBACK lpWABPCO) { // set up the print dialog stuff // Call the common print dialog to get the default PRINTDLGEX pd={0}; pd.lStructSize = sizeof(PRINTDLGEX); pd.hwndOwner = hWnd; pd.hDevMode = (HANDLE) NULL; pd.hDevNames = (HANDLE) NULL; pd.hDC = (HDC) NULL; pd.Flags = PD_RETURNDC | // return PrintDC PD_DISABLEPRINTTOFILE | PD_ENABLEPRINTTEMPLATE | PD_HIDEPRINTTOFILE | PD_NOPAGENUMS; pd.Flags2 = 0; if(ListView_GetSelectedCount(hWndLV) > 0) pd.Flags |= PD_SELECTION; else pd.Flags |= PD_NOSELECTION; pd.nCopies = 1; pd.hInstance = hinstMapiX; pd.lpPrintTemplateName = MAKEINTRESOURCE(IDD_DIALOG_PRINTDLGEX); //(LPSTR) NULL; pd.lpCallback = (LPUNKNOWN)lpWABPCO; // app callback interface pd.nPropertyPages = 0; pd.lphPropertyPages = NULL; pd.nStartPage = START_PAGE_GENERAL; *lpPD = pd; return; } //$$//////////////////////////////////////////////////////////////////////////////////////////// // // // SetPrintDlgStruct - Fills in the default PD values // // hWnd - HWND of parent dialog // PD - PrintDLG struct // hWndLV - HWND of listView to print from - if there are no selections in the list view, // the selections option is turned off in the print dialog // //////////////////////////////////////////////////////////////////////////////////////////////// void SetPrintDlgStruct(HWND hWnd, PRINTDLG * lpPD, HWND hWndLV, LPARAM lCustData) { // set up the print dialog stuff // Call the common print dialog to get the default PRINTDLG pd={0}; pd.lStructSize = sizeof(PRINTDLG); pd.hDevMode = (HANDLE) NULL; pd.hDevNames = (HANDLE) NULL; pd.Flags = PD_RETURNDC | // return PrintDC PD_NOPAGENUMS | // Disable Page number option PD_DISABLEPRINTTOFILE | PD_HIDEPRINTTOFILE | PD_ENABLEPRINTHOOK | PD_ENABLEPRINTTEMPLATE; if(ListView_GetSelectedCount(hWndLV) > 0) pd.Flags |= PD_SELECTION; else pd.Flags |= PD_NOSELECTION; pd.hwndOwner = hWnd; pd.hDC = (HDC) NULL; pd.nFromPage = 1; pd.nToPage = 1; pd.nMinPage = 0; pd.nMaxPage = 0; pd.nCopies = 1; pd.hInstance = hinstMapiX; pd.lCustData = lCustData; pd.lpfnPrintHook = (LPPRINTHOOKPROC) &fnPrintDialogProc; //NULL; pd.lpfnSetupHook = (LPSETUPHOOKPROC) NULL; pd.lpPrintTemplateName = MAKEINTRESOURCE(IDD_DIALOG_PRINTDLGORD); //(LPSTR) NULL; pd.lpSetupTemplateName = (LPTSTR) NULL; pd.hPrintTemplate = (HANDLE) NULL; pd.hSetupTemplate = (HANDLE) NULL; *lpPD = pd; return; } /* - - HrGetPrintData - Determines whether to show the new print dialog or the old print dialog Fills in all the structures appropriately and returns the values we care about such as nCopies, Print style, etc * * */ HRESULT HrGetPrintData(LPADRBOOK lpAdrBook, HWND hWndParent, HWND hWndLV, HDC * lphdcPrint, int * lpnCopies, DWORD * lpdwStyle, DWORD * lpdwRange) { DWORD dwSelectedStyle = styleMemo; HRESULT hr = S_OK; LPWABPRINTDIALOGCALLBACK lpWABPCO = NULL; PRINTDLG pd = {0}; PRINTDLGEX pdEx = {0}; // Test for presence of NT5 PrintDlgEx if(!HR_FAILED(hr = PrintDlgEx(NULL))) { if(HR_FAILED(hr = HrCreatePrintCallbackObject((LPIAB)lpAdrBook,&lpWABPCO,dwSelectedStyle))) goto out; if(!lpWABPCO) { hr = E_FAIL; goto out; } SetPrintDlgExStruct(hWndParent, &pdEx, hWndLV, lpWABPCO); if(HR_FAILED(hr = PrintDlgEx(&pdEx))) { DebugTrace( TEXT("PrintDlgEx returns 0x%.8x\n"),hr); // #98841 Millenium returns fail in this case, but for PrintDlgEx(NULL) it returns S_OK (YST) goto doOldPrint; } *lphdcPrint = pdEx.hDC; *lpnCopies = pdEx.nCopies; *lpdwStyle = lpWABPCO->dwSelectedStyle; if (pdEx.Flags & PD_SELECTION) *lpdwRange = rangeSelected; else *lpdwRange = rangeAll; } else { doOldPrint: SetPrintDlgStruct(hWndParent, &pd, hWndLV, (LPARAM) &dwSelectedStyle); // Show the print dialog if(!PrintDlg(&pd)) goto out; *lphdcPrint = pd.hDC; *lpnCopies = pd.nCopies; *lpdwStyle = dwSelectedStyle; if (pd.Flags & PD_SELECTION) *lpdwRange = rangeSelected; else *lpdwRange = rangeAll; hr = S_OK; } out: if(lpWABPCO) lpWABPCO->lpVtbl->Release(lpWABPCO); return hr; } //$$//////////////////////////////////////////////////////////////////////////////// // // HrPrintItems - Prints selected contacts // Prints the contents of the address book // Pops up a dialog that lets user select what he wants to print // Options are (or will be) // All or selected // Style - memo, business, or phone book // // Items are printed using the current sort style in the list view // ////////////////////////////////////////////////////////////////////////////////////// HRESULT HrPrintItems( HWND hWnd, LPADRBOOK lpAdrBook, HWND hWndLV, BOOL bCurrentSortisByLastName) { HRESULT hr = E_FAIL; HWND hWndRE = NULL; // we'll do our formating in a riceh edit control and use that for printing PRINTINFO *ppi=0; BOOL fStartedDoc = FALSE; BOOL fStartedPage= FALSE; DOCINFO docinfo={0}; HCURSOR hOldCur = NULL; int i,nCode; HINSTANCE hRELib = NULL; LPPTGDATA lpPTGData=GetThreadStoragePointer(); LPIAB lpIAB = (LPIAB)lpAdrBook; HDC hdcPrint = NULL; int nCopies = 0; DWORD dwStyle; DWORD dwRange; // Double check if there are any print extensions that we need to accomodate // if(bCheckForPrintExtensions(NULL, 0)) { // Found a print extension hr = HrUseWABPrintExtension(hWnd, lpAdrBook, hWndLV); goto out; } if(!(ppi = LocalAlloc(LMEM_ZEROINIT, sizeof(PRINTINFO)))) goto out; ppi->hwndDlg = hWnd; if(HR_FAILED(HrGetPrintData(lpAdrBook, hWnd, hWndLV, &hdcPrint, &nCopies, &dwStyle, &dwRange))) goto out; if(!hdcPrint) goto out; // Take care of zilch copies // // Actually it seems that this number is meaningless if the printer can handle multiple // copies. If the printer can't handle multiple copies, we will get info in this number. // if(!nCopies) nCopies = 1; ppi->hdcPrn = hdcPrint; // Create a RichEdit control in which we will do our formatting hRELib = LoadLibrary( TEXT("riched20.dll")); if(!hRELib) { hRELib = LoadLibrary( TEXT("riched32.dll")); s_bUse20 = FALSE; } //IF_WIN16(hRELib = LoadLibrary( TEXT("riched.dll"));) if(!hRELib) goto out; hWndRE = CreateWindowEx(0, (s_bUse20 ? RICHEDIT_CLASS : TEXT("RichEdit")), TEXT(""),WS_CHILD | ES_MULTILINE, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, hWnd,(HMENU) NULL,hinstMapiX,NULL); if (!hWndRE) goto out; ////////////////////////// { CHARFORMAT cf = {0}; TCHAR rgch[CCHMAX_STRINGRES]; DWORD dwCodepage ; CHARSETINFO rCharsetInfo; LOGFONT lfSystem; BOOL bNeedLargeFont = FALSE; cf.cbSize = sizeof(cf); cf.dwMask = CFM_FACE; SendMessage(hWndRE, EM_GETCHARFORMAT, (WPARAM) TRUE, (LPARAM) &cf); if(LoadString(hinstMapiX, idsDefaultFontFace, rgch, ARRAYSIZE(rgch))) StrCpyN(cf.szFaceName, rgch, ARRAYSIZE(cf.szFaceName)); else StrCpyN(cf.szFaceName, szDefFont, ARRAYSIZE(cf.szFaceName)); // [a-msadek] bug#56478 // Arail does not support Thai so as all other base fonts // The best way to determine the OS language is from system font charset if(GetObject(GetStockObject(SYSTEM_FONT), sizeof(lfSystem), (LPVOID)&lfSystem)) { if (lfSystem.lfCharSet == THAI_CHARSET) { StrCpyN(cf.szFaceName, szThaiDefFont, ARRAYSIZE(cf.szFaceName)); // Thai Font sizes are always smaller than English as the vowl and tone // markes consumes some of the font height bNeedLargeFont = TRUE; } } // bug #53058 - set correct CharSet info for Eastern European dwCodepage = GetACP(); // Get GDI charset info if ( dwCodepage != 1252 && TranslateCharsetInfo((LPDWORD) IntToPtr(dwCodepage) , &rCharsetInfo, TCI_SRCCODEPAGE)) cf.bCharSet = (BYTE) rCharsetInfo.ciCharset; SendMessage(hWndRE, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf); if(bNeedLargeFont) { ReduceFontCmd(hWndRE, FALSE, 80, TRUE); } } ////////////////////////// // At the top of the print job, print the header with the users name or with // the default TEXT("Windows Address Book") title { TCHAR szHead[MAX_PATH]; DWORD dwLen = ARRAYSIZE(szHead); SCODE sc; *szHead = '\0'; if(bIsThereACurrentUser(lpIAB) && lstrlen(lpIAB->szProfileName)) StrCpyN(szHead, lpIAB->szProfileName, ARRAYSIZE(szHead)); else GetUserName(szHead, &dwLen); if(!lstrlen(szHead)) LoadString(hinstMapiX, IDS_ADDRBK_CAPTION, szHead, ARRAYSIZE(szHead)); if (( sc = ScInitPrintInfo( ppi, hWnd, szHead, &g_rcBorder, hWndRE)) != S_OK) goto out; } // While the printing is in progress, we dont want the user to mess with the // listview selection otherwise the print job will print the wrong entries // Hence we disable this window (since the print cancel dialog is really a modeless dialog) EnableWindow(hWnd, FALSE); CreateShowAbortDialog(hWnd, 0, 0, ListView_GetSelectedCount(hWndLV), 0); // Format the prop data into the Rich Edit Control if(!WABFormatData(lpAdrBook, hWnd, hWndRE, hWndLV, dwRange, dwStyle, ppi, bCurrentSortisByLastName)) goto out; if(bTimeToAbort()) goto out; for(i=0;ihdcPrn, ppi->pfnAbortProc)) <= 0) { hr = E_FAIL; break; } if(bTimeToAbort()) goto out; // Start a print job if (StartDoc(ppi->hdcPrn, &docinfo) <= 0) { DebugPrintError(( TEXT("StartDoc failed: %d\n"), GetLastError())); goto out; } fStartedDoc = TRUE; //StartPage(pd.hDC); if(bTimeToAbort()) goto out; // Go on and print the message! if (ScPrintMessage(ppi, hWndRE) != S_OK) goto out; if(bTimeToAbort()) goto out; // End the page if(ScGetNextBand( ppi, FALSE) != S_OK) goto out; if(bTimeToAbort()) goto out; // Finish up the print job if it had been started if (fStartedDoc) { EndDoc(ppi->hdcPrn); fStartedDoc = FALSE; } } hr = hrSuccess; out: if(hWndRE) { SendMessage(hWndRE, WM_SETTEXT, 0, (LPARAM)szEmpty); SendMessage(hWndRE, WM_CLEAR, 0, 0); } if(bTimeToAbort()) { hr = MAPI_E_USER_CANCEL; pt_bPrintUserAbort = FALSE; } // Re-enable the window and ensure it stays put EnableWindow(hWnd, TRUE); //SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER); CloseAbortDlg(); // Finish up the print job if it had been started if (fStartedDoc) EndDoc(ppi->hdcPrn); // Get rid of our Rich Edit control if (hWndRE) DestroyWindow(hWndRE); if(hOldCur) SetCursor(hOldCur); if(ppi) { if(ppi->hfontPlain) DeleteObject(ppi->hfontPlain); if(ppi->hfontBold) DeleteObject(ppi->hfontBold); if(ppi->hfontSep) DeleteObject(ppi->hfontSep); LocalFreeAndNull(&ppi); } if(hRELib) FreeLibrary((HMODULE) hRELib); return hr; } /* * Handles the WM_INITDIALOG for both PrintDlg and PrintDlgEx */ BOOL bHandleWMInitDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, LPDWORD lpdwStyle) { DWORD dwStyle = lpdwStyle ? *lpdwStyle : styleMemo; int nID; switch (dwStyle) { case styleBusinessCard: nID = IDC_PRINT_RADIO_CARD; break; case stylePhoneList: nID = IDC_PRINT_RADIO_PHONELIST; break; default: case styleMemo: nID = IDC_PRINT_RADIO_MEMO; //default break; } CheckRadioButton( hDlg, IDC_PRINT_RADIO_MEMO, IDC_PRINT_RADIO_PHONELIST, nID); SetFocus(hDlg); return 0; } /* * Handles the WM_COMMAND for both PrintDlg and PrintDlgEx */ BOOL bHandleWMCommand(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, LPDWORD lpdwStyle ) { switch (GET_WM_COMMAND_ID(wParam,lParam)) { case IDC_PRINT_RADIO_MEMO: //lpPD->lCustData = (DWORD) styleMemo; *lpdwStyle = (DWORD) styleMemo; break; case IDC_PRINT_RADIO_CARD: //lpPD->lCustData = (DWORD) styleBusinessCard; *lpdwStyle = (DWORD) styleBusinessCard; break; case IDC_PRINT_RADIO_PHONELIST: //lpPD->lCustData = (DWORD) stylePhoneList; *lpdwStyle = (DWORD) stylePhoneList; break; } return 0; } /* * Handles the WM_HELP for both PrintDlg and PrintDlgEx */ BOOL bHandleWMHelp(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, LPDWORD lpdwStyle ) { int id = ((LPHELPINFO)lParam)->iCtrlId; if( id == IDC_PRINT_FRAME_STYLE || id == IDC_PRINT_RADIO_MEMO || id == IDC_PRINT_RADIO_CARD || id == IDC_PRINT_RADIO_PHONELIST) { WABWinHelp(((LPHELPINFO)lParam)->hItemHandle, g_szWABHelpFileName, HELP_WM_HELP, (DWORD_PTR)(LPSTR) rgPrintHelpIDs ); } return FALSE; } /* * Handles the WM_CONTEXTMENU for both PrintDlg and PrintDlgEx */ BOOL bHandleWMContextMenu(HWND hDlg, UINT message, WPARAM wParam,LPARAM lParam,LPDWORD lpdwStyle ) { HWND hwnd = (HWND) wParam; if( hwnd == GetDlgItem(hDlg, IDC_PRINT_FRAME_STYLE) || hwnd == GetDlgItem(hDlg, IDC_PRINT_RADIO_MEMO) || hwnd == GetDlgItem(hDlg, IDC_PRINT_RADIO_CARD) || hwnd == GetDlgItem(hDlg, IDC_PRINT_RADIO_PHONELIST) ) { WABWinHelp((HWND) wParam, g_szWABHelpFileName, HELP_CONTEXTMENU, (DWORD_PTR)(LPVOID) rgPrintHelpIDs ); } return FALSE; } //$$***************************************************************** // // FUNCTION: fnPrintDialogProc // // PURPOSE: Printer dialog hook procedure // // We have modified the CommDlg print template with the // WAB print styles. // This is a hook procedure that takes care of our // newly added controls // // When WM_INITDIALOG is called, the lParam contains a pointer to the // PRINTDLG structure controling the print setup dialog // //******************************************************************* INT_PTR CALLBACK fnPrintDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { LPDWORD lpdwStyle = (LPDWORD) GetWindowLongPtr(hDlg, DWLP_USER); switch(message) { case WM_INITDIALOG: { LPPRINTDLG lpPD = (LPPRINTDLG) lParam; #ifdef WIN16 // Strange situation here. If I don't create edt1 and edt2 which are used for page range, entire print dialog work incorrectly. // So I just add two controls(edt1 and edt2) and hide those here. ShowWindow(GetDlgItem(hDlg, edt1), SW_HIDE); ShowWindow(GetDlgItem(hDlg, edt2), SW_HIDE); #endif if(lpPD) { lpdwStyle = (LPDWORD) lpPD->lCustData; SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM)lpdwStyle); //Save this for future reference return bHandleWMInitDialog(hDlg,message,wParam,lParam,lpdwStyle); } } SetFocus(hDlg); return 0; break; case WM_COMMAND: if(lpdwStyle) return bHandleWMCommand(hDlg,message,wParam,lParam,lpdwStyle); break; case WM_HELP: return bHandleWMHelp(hDlg,message,wParam,lParam,lpdwStyle); break; #ifndef WIN16 case WM_CONTEXTMENU: return bHandleWMContextMenu(hDlg,message,wParam,lParam,lpdwStyle); break; #endif // !WIN16 default: return FALSE; break; } return FALSE; } /************************************************************************************************** * ScInitPrintInfo * * Purpose: * Initialize the fields of a print info structure relevant for * the actual printing. * * Arguments: * ppi Pointer to the PRINTINFO structure * hwnd The owner of the print dialog box * szHeader The string to be printed at the top of each * message * prcBorder Pointer to a rect whose fields contain the * number of twips to use as a margin around the * printed text. * hWndRE rich edit control in which we will do our formatting * * Returns: * SCODE indicating success or failure * ***************************************************************************************************/ SCODE ScInitPrintInfo( PRINTINFO * ppi, HWND hwnd, LPTSTR szHeader, RECT * prcBorder, HWND hWndRE) { SIZE sizeExt; LOGFONT logfont = {0}; HFONT hfontOld; TCHAR szT[20]; SCODE sc = S_OK; TCHAR rgch[CCHMAX_STRINGRES]; // Save handle to our parent window ppi->hwnd = hwnd; // Save a pointer to our header string ppi->szHeader = szHeader; // Set up pointer to our abort procedure ppi->pfnAbortProc = FAbortProc; ppi->hwndRE = hWndRE; // Determine the page size in pixels ppi->sizePage.cx = GetDeviceCaps(ppi->hdcPrn, HORZRES); ppi->sizePage.cy = GetDeviceCaps(ppi->hdcPrn, VERTRES); // Exchange 13497: If we have nothing to render to abort now. if (!ppi->sizePage.cx || !ppi->sizePage.cy) { sc = E_FAIL; goto CleanUp; } ///MoveWindow(hWndRE, 0, 0, ppi->sizepage.cx, ppi->sizepage.cy, FALSE); // Determine the number of pixels in a logical inch ppi->sizeInch.cx = GetDeviceCaps(ppi->hdcPrn, LOGPIXELSX); ppi->sizeInch.cy = GetDeviceCaps(ppi->hdcPrn, LOGPIXELSY); // Exchange 13497: If we failed to get some info make some assumptions. // At worst assume 300 dpi. if (!ppi->sizeInch.cx) ppi->sizeInch.cx = ppi->sizeInch.cy ? ppi->sizeInch.cy : 300; if (!ppi->sizeInch.cy) ppi->sizeInch.cy = 300; //$ Raid 2667: Determine if we still fit within the page in twips if (LPixelsToTwips(ppi->sizePage.cx, ppi->sizeInch.cx) > INT_MAX || LPixelsToTwips(ppi->sizePage.cy, ppi->sizeInch.cy) > INT_MAX) { sc = E_FAIL; goto CleanUp; } // Set up the margin settings ppi->rcMargin.top = NTwipsToPixels(prcBorder->top, ppi->sizeInch.cy); ppi->rcMargin.bottom = ppi->sizePage.cy - NTwipsToPixels(prcBorder->bottom, ppi->sizeInch.cy); if (ppi->rcMargin.bottom < ppi->rcMargin.top) { // Bottom is above top. Top/Bottom margins ignored ppi->rcMargin.top = 0; ppi->rcMargin.bottom = ppi->sizePage.cy; } ppi->rcMargin.left = NTwipsToPixels(prcBorder->left, ppi->sizeInch.cx); ppi->rcMargin.right = ppi->sizePage.cx - NTwipsToPixels(prcBorder->right, ppi->sizeInch.cx); if (ppi->rcMargin.right < ppi->rcMargin.left) { // Right is left of left. Left/Right margins ignored ppi->rcMargin.left = 0; ppi->rcMargin.right = ppi->sizePage.cx; } // Set up the separator font //$ Raid 2773: Let user customize separator font logfont.lfHeight = - cySepFontSize(ppi); logfont.lfWeight = FW_BOLD; logfont.lfCharSet = DEFAULT_CHARSET; if (LoadString(hinstMapiX, idsDefaultFontFace, rgch, ARRAYSIZE(rgch))) StrCpyN(logfont.lfFaceName, rgch, ARRAYSIZE(logfont.lfFaceName)); else StrCpyN(logfont.lfFaceName, szDefFont, ARRAYSIZE(logfont.lfFaceName)); ppi->hfontSep = CreateFontIndirect(&logfont); // Set up common font ZeroMemory(&logfont, sizeof(LOGFONT)); logfont.lfHeight = - 10 * ppi->sizeInch.cy / cPtsPerInch; logfont.lfWeight = FW_NORMAL; logfont.lfCharSet = DEFAULT_CHARSET; if(LoadString(hinstMapiX, idsDefaultFontFace, rgch, ARRAYSIZE(rgch))) StrCpyN(logfont.lfFaceName, rgch, ARRAYSIZE(logfont.lfFaceName)); else StrCpyN(logfont.lfFaceName, szDefFont, ARRAYSIZE(logfont.lfFaceName)); ppi->hfontPlain = CreateFontIndirect(&logfont); logfont.lfWeight = FW_BOLD; ppi->hfontBold = CreateFontIndirect(&logfont); // Calculate where to put the footer // Load up the formatting string to use for page numbers //LoadString(hinstMapiX, idsFmtPageNumber, ppi->szPageNumber, ARRAYSIZE(ppi->szPageNumber)); StrCpyN(ppi->szPageNumber, TEXT("%d"), ARRAYSIZE(ppi->szPageNumber)); wnsprintf(szT, ARRAYSIZE(szT), ppi->szPageNumber, ppi->lPageNumber); // Sample the height hfontOld = (HFONT)SelectObject(ppi->hdcPrn, ppi->hfontPlain); GetTextExtentPoint(ppi->hdcPrn, szT, lstrlen(szT), &sizeExt); ppi->yFooter = ppi->rcMargin.bottom - sizeExt.cy; SelectObject(ppi->hdcPrn, hfontOld); // Make sure our footer doesn't go above the top of the page if (ppi->yFooter < ppi->rcMargin.top) sc = E_FAIL; CleanUp: return sc; } //$$//////////////////////////////////////// // // GetNumberFromStringResource // //////////////////////////////////////////// int GetNumberFromStringResource(int idNumString) { TCHAR szBuf[MAX_PATH]; if (LoadString(hinstMapiX, idNumString, szBuf, ARRAYSIZE(szBuf))) return my_atoi(szBuf); else return 0; } /*--------------------------------------------------------------------------------------------------*/ /*--IPrintDialogCallback stuff----------------------------------------------------------------------*/ /*--Special stuff for the new NT5 Print Dialog------------------------------------------------------*/ /*--------------------------------------------------------------------------------------------------*/ WAB_PRINTDIALOGCALLBACK_Vtbl vtblWABPRINTDIALOGCALLBACK = { VTABLE_FILL WAB_PRINTDIALOGCALLBACK_QueryInterface, WAB_PRINTDIALOGCALLBACK_AddRef, WAB_PRINTDIALOGCALLBACK_Release, WAB_PRINTDIALOGCALLBACK_InitDone, WAB_PRINTDIALOGCALLBACK_SelectionChange, WAB_PRINTDIALOGCALLBACK_HandleMessage }; /* - HrCreatePrintCallbackObject - * * This callback object is needed so the new NT5 print dialog can provide send messages back to * us for the customization we do for the print dialog .. * */ HRESULT HrCreatePrintCallbackObject(LPIAB lpIAB, LPWABPRINTDIALOGCALLBACK * lppWABPCO, DWORD dwSelectedStyle) { LPWABPRINTDIALOGCALLBACK lpWABPCO = NULL; SCODE sc; HRESULT hr = hrSuccess; // // Allocate space for the IAB structure // if (FAILED(sc = MAPIAllocateBuffer(sizeof(WABPRINTDIALOGCALLBACK), (LPVOID *) &lpWABPCO))) { hr = ResultFromScode(sc); goto err; } MAPISetBufferName(lpWABPCO, TEXT("WAB Print Dialog Callback Object")); ZeroMemory(lpWABPCO, sizeof(WABPRINTDIALOGCALLBACK)); lpWABPCO->lpVtbl = &vtblWABPRINTDIALOGCALLBACK; lpWABPCO->lpIAB = lpIAB; lpWABPCO->dwSelectedStyle = dwSelectedStyle; lpWABPCO->lpVtbl->AddRef(lpWABPCO); *lppWABPCO = lpWABPCO; return(hrSuccess); err: FreeBufferAndNull(&lpWABPCO); return(hr); } void ReleaseWABPrintCallbackObject(LPWABPRINTDIALOGCALLBACK lpWABPCO) { MAPIFreeBuffer(lpWABPCO); } STDMETHODIMP_(ULONG) WAB_PRINTDIALOGCALLBACK_AddRef(LPWABPRINTDIALOGCALLBACK lpWABPCO) { return(++(lpWABPCO->lcInit)); } STDMETHODIMP_(ULONG) WAB_PRINTDIALOGCALLBACK_Release(LPWABPRINTDIALOGCALLBACK lpWABPCO) { ULONG ulc = (--(lpWABPCO->lcInit)); if(ulc==0) ReleaseWABPrintCallbackObject(lpWABPCO); return(ulc); } STDMETHODIMP WAB_PRINTDIALOGCALLBACK_QueryInterface(LPWABPRINTDIALOGCALLBACK lpWABPCO, REFIID lpiid, LPVOID * lppNewObj) { LPVOID lp = NULL; if(!lppNewObj) return MAPI_E_INVALID_PARAMETER; *lppNewObj = NULL; if(IsEqualIID(lpiid, &IID_IUnknown)) lp = (LPVOID) lpWABPCO; if(IsEqualIID(lpiid, &IID_IPrintDialogCallback)) lp = (LPVOID) lpWABPCO; if(!lp) return E_NOINTERFACE; ((LPWABPRINTDIALOGCALLBACK) lp)->lpVtbl->AddRef((LPWABPRINTDIALOGCALLBACK) lp); *lppNewObj = lp; return S_OK; } STDMETHODIMP WAB_PRINTDIALOGCALLBACK_InitDone(LPWABPRINTDIALOGCALLBACK lpWABPCO) { HRESULT hr = S_FALSE; DebugTrace( TEXT("WAB_PRINTDIALOGCALLBACK_InitDone\n")); return hr; } STDMETHODIMP WAB_PRINTDIALOGCALLBACK_SelectionChange(LPWABPRINTDIALOGCALLBACK lpWABPCO) { HRESULT hr = S_FALSE; DebugTrace( TEXT("WAB_PRINTDIALOGCALLBACK_SelectionChange\n")); return hr; } STDMETHODIMP WAB_PRINTDIALOGCALLBACK_HandleMessage(LPWABPRINTDIALOGCALLBACK lpWABPCO, HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pResult) { BOOL bRet = FALSE; LPDWORD lpdwStyle = &lpWABPCO->dwSelectedStyle; DebugTrace( TEXT("WAB_PRINTDIALOGCALLBACK_HandleMessage: 0x%.8x\n"), message); switch(message) { case WM_INITDIALOG: bRet = bHandleWMInitDialog(hDlg,message,wParam,lParam,lpdwStyle); break; case WM_COMMAND: bRet = bHandleWMCommand(hDlg,message,wParam,lParam,lpdwStyle); break; case WM_HELP: bRet = bHandleWMHelp(hDlg,message,wParam,lParam,lpdwStyle); break; case WM_CONTEXTMENU: bRet = bHandleWMContextMenu(hDlg,message,wParam,lParam,lpdwStyle); break; default: bRet = FALSE; break; } return (bRet ? S_OK : S_FALSE); } /******************************************************************************************/ /* - bCheckForPrintExtensions - * In case any app has implemented a Print Extension to the WAB, we should hook into * that print extension * * lpDLLPath can be NULL or should point to a buffer big enough to receive the module Path * */ static const LPTSTR szExtDisplayMailUser = TEXT("Software\\Microsoft\\WAB\\WAB4\\ExtPrint"); extern HrGetActionAdrList(LPADRBOOK lpAdrBook,HWND hWndLV,LPADRLIST * lppAdrList,LPTSTR * lppURL, BOOL * lpbIsNTDSEntry); BOOL bCheckForPrintExtensions(LPTSTR lpDLLPath, DWORD cchSize) { BOOL bRet = FALSE; HKEY hKey = NULL; if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,szExtDisplayMailUser,0, KEY_READ,&hKey)) { goto out; } { TCHAR szExt[MAX_PATH]; DWORD dwIndex = 0, dwSize = ARRAYSIZE(szExt), dwType = 0; *szExt = '\0'; while(ERROR_SUCCESS == RegEnumValue(hKey, dwIndex, szExt, &dwSize, 0, &dwType, NULL, NULL)) { // we found some entry in here .. the value name will be the full path // to the module containing the print function // Double-check that this module actually exists if (szExt && lstrlen(szExt) && (GetFileAttributes(szExt) != 0xFFFFFFFF)) { if(lpDLLPath) StrCpyN(lpDLLPath, szExt, cchSize); bRet = TRUE; goto out; } } } out: if(hKey) RegCloseKey(hKey); return bRet; } /* - - HrUseWABPrintExtension() - * Loads the WAB Print Extension from the extension DLL * and calls into it * * hWnd - Handle of WAB parent * lpAdrBook - lpAdrBook pointer * hWndLV - listview from which a user may have chosen selections * */ HRESULT HrUseWABPrintExtension(HWND hWnd, LPADRBOOK lpAdrBook, HWND hWndLV) { TCHAR szExt[MAX_PATH]; HRESULT hr = E_FAIL; HINSTANCE hInstPrint = NULL; LPWABPRINTEXT lpfnWABPrintExt = NULL; LPADRLIST lpAdrList = NULL; LPWABOBJECT lpWABObject = (LPWABOBJECT)((LPIAB)lpAdrBook)->lpWABObject; *szExt = '\0'; if(!bCheckForPrintExtensions(szExt, ARRAYSIZE(szExt)) || !lstrlen(szExt)) goto out; if(!(hInstPrint = LoadLibrary(szExt))) goto out; lpfnWABPrintExt = (LPWABPRINTEXT) GetProcAddress(hInstPrint, "WABPrintExt"); if(!lpfnWABPrintExt) goto out; // Get the currently selected data from the list view if(HR_FAILED(hr = HrGetActionAdrList(lpAdrBook,hWndLV,&lpAdrList,NULL,NULL))) goto out; hr = lpfnWABPrintExt(lpAdrBook, lpWABObject, hWnd, lpAdrList); out: if(lpAdrList) FreePadrlist(lpAdrList); if(hInstPrint) FreeLibrary(hInstPrint); return hr; }