prqwnd.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 45k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "shellprv.h"
  2. #pragma  hdrstop
  3. #include "printer.h"
  4. #ifndef WINNT
  5. #define ICOL_DOCNAME    0
  6. #define ICOL_STATUS     1
  7. #define ICOL_OWNER      2
  8. #define ICOL_PROGRESS   3
  9. #define ICOL_TIME       4
  10. #define ICOL_NUM        5
  11. typedef struct _QueueInfo
  12. {
  13.     JOB_INFO_2 *pJobs;
  14.     DWORD dwJobs;
  15.     IDPRINTER idp;              // name of printer
  16.     HANDLE hPrinter;            // handle to printer
  17.     HWND hDlg;                  // dialog handle
  18.     HWND hwndLV;                // listview handle
  19.     HWND hwndSB;                // status bar handle
  20.     BOOL fStatusBar;            // TRUE iff status bar is shown
  21.     int cx, cy;                 // window extents for WM_SIZE
  22.     BOOL bDragSource;
  23.     BOOL bMinimized;
  24.     // POINT ptDragAnchor;
  25.     HICON hLargeIcon;           // icons for the sysmenu / alt-tab list
  26.     HICON hSmallIcon;           // these need to be freed on window destroy
  27. } QUEUEINFO, *PQUEUEINFO;
  28. typedef struct _PosInfo
  29. {
  30.     WINDOWPLACEMENT wp;
  31.     int nWidths[ICOL_NUM];
  32.     BOOL fStatusBar;
  33. } POSINFO;
  34. typedef struct _InitInfo
  35. {
  36.     LPCTSTR lpszPrinterName;
  37.     int nCmdShow;
  38. } INITINFO;
  39. extern TCHAR const c_szRAW[];
  40. BOOL Printer_ModifyJob(HWND hDlg, DWORD dwCommand);
  41. #endif // WINNT
  42. //---------------------------------------------------------------------------
  43. //
  44. // Helper functions
  45. //
  46. typedef struct _IDPRINTJOB
  47. {
  48.     USHORT              cb;
  49.     SHCNF_PRINTJOB_DATA data;
  50.     USHORT              uTerm;
  51. } IDPRINTJOB, *LPIDPRINTJOB;
  52. typedef const IDPRINTJOB *LPCIDPRINTJOB;
  53. void Printjob_FillPidl(LPIDPRINTJOB pidl, LPSHCNF_PRINTJOB_DATA pData)
  54. {
  55.     pidl->cb = FIELD_OFFSET(IDPRINTJOB, uTerm);
  56.     if (pData)
  57.     {
  58.         pidl->data = *pData;
  59.     }
  60.     else
  61.     {
  62.         ZeroMemory(&(pidl->data), SIZEOF(SHCNF_PRINTJOB_DATA));
  63.     }
  64.     pidl->uTerm = 0;
  65. }
  66. LPITEMIDLIST Printjob_GetPidl(LPCTSTR szName, LPSHCNF_PRINTJOB_DATA pData)
  67. {
  68.     LPITEMIDLIST pidl = NULL;
  69.     LPITEMIDLIST pidlParent = Printers_GetPidl(NULL, szName);
  70.     if (pidlParent)
  71.     {
  72.         IDPRINTJOB idj;
  73.         Printjob_FillPidl(&idj, pData);
  74.         pidl = ILCombine(pidlParent, (LPITEMIDLIST)&idj);
  75.         ILFree(pidlParent);
  76.     }
  77.     return pidl;
  78. }
  79. #ifndef WINNT
  80. /********************************************************************/
  81. /* IDropTarget and IDataObject for drag & drop re-ordering of queue */
  82. /********************************************************************/
  83. // print job descriptor
  84. typedef struct
  85. {
  86.     DWORD   dwJobId;
  87.     int     iItem;
  88. } PRINTJOBDESC;
  89. // data object for print job
  90. typedef struct
  91. {
  92.     IDataObject dtobj;
  93.     UINT        cRef;
  94.     PRINTJOBDESC pj;    // the data is here
  95. } CPJData;
  96. UINT g_cfPrintJob = 0;
  97. extern const IDataObjectVtbl c_CPJDataVtbl;          // forward
  98. void RegisterPrintJobFormat()
  99. {
  100.     if (!g_cfPrintJob)
  101.         g_cfPrintJob = RegisterClipboardFormat(TEXT("PrintJob"));
  102. }
  103. //
  104. // print job data object for drag/drop
  105. //
  106. STDMETHODIMP CPJData_CreateInstance(const PRINTJOBDESC *ppj, IDataObject **ppdtobj)
  107. {
  108.     CPJData *this = (void*)LocalAlloc(LPTR, SIZEOF(CPJData));
  109.     if (this)
  110.     {
  111.         this->dtobj.lpVtbl = &c_CPJDataVtbl;
  112.         this->cRef = 1;
  113.         *ppdtobj = &this->dtobj;
  114.         this->pj = *ppj;
  115.         RegisterPrintJobFormat();
  116.         return NOERROR;
  117.     }
  118.     else
  119.     {
  120.         *ppdtobj = NULL;
  121.         return E_OUTOFMEMORY;
  122.     }
  123. }
  124. //===========================================================================
  125. STDMETHODIMP CPJData_QueryInterface(IDataObject *pdtobj, REFIID riid, LPVOID *ppvObj)
  126. {
  127.     CPJData *this = IToClass(CPJData, dtobj, pdtobj);
  128.     if (IsEqualIID(riid, &IID_IDataObject) || IsEqualIID(riid, &IID_IUnknown))
  129.     {
  130.         *ppvObj = this;
  131.         this->cRef++;
  132.         return NOERROR;
  133.     }
  134.     *ppvObj = NULL;
  135.     return E_NOINTERFACE;
  136. }
  137. STDMETHODIMP_(ULONG) CPJData_AddRef(IDataObject *pdtobj)
  138. {
  139.     CPJData *this = IToClass(CPJData, dtobj, pdtobj);
  140.     this->cRef++;
  141.     return this->cRef;
  142. }
  143. STDMETHODIMP_(ULONG) CPJData_Release(IDataObject *pdtobj)
  144. {
  145.     CPJData *this = IToClass(CPJData, dtobj, pdtobj);
  146.     if (InterlockedDecrement(&this->cRef))
  147.         return this->cRef;
  148.     LocalFree((HLOCAL)this);
  149.     return 0;
  150. }
  151. STDMETHODIMP CPJData_GetData(IDataObject *pdtobj, LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium)
  152. {
  153.     CPJData *this = IToClass(CPJData, dtobj, pdtobj);
  154.     HRESULT hres = E_INVALIDARG;
  155.     pmedium->hGlobal = NULL;
  156.     pmedium->pUnkForRelease = NULL;
  157.     pmedium->tymed = TYMED_HGLOBAL;
  158.     if ((g_cfPrintJob == pformatetcIn->cfFormat) && (TYMED_HGLOBAL & pformatetcIn->tymed))
  159.     {
  160.         pmedium->hGlobal = GlobalAlloc(GPTR, SIZEOF(PRINTJOBDESC));
  161.         if (pmedium->hGlobal)
  162.         {
  163.             *((PRINTJOBDESC *)pmedium->hGlobal) = this->pj;
  164.             hres = NOERROR;     // success
  165.         }
  166.         else
  167.             hres = E_OUTOFMEMORY;
  168.     }
  169.     return hres;
  170. }
  171. STDMETHODIMP CPJData_GetDataHere(IDataObject *pdtobj, LPFORMATETC pformatetc, LPSTGMEDIUM pmedium )
  172. {
  173.     return E_NOTIMPL;
  174. }
  175. STDMETHODIMP CPJData_QueryGetData(IDataObject *pdtobj, LPFORMATETC pformatetcIn)
  176. {
  177.     // CPJData *this = IToClass(CPJData, dtobj, pdtobj);
  178.     if ((g_cfPrintJob == pformatetcIn->cfFormat) && (TYMED_HGLOBAL & pformatetcIn->tymed))
  179.     {
  180.         return NOERROR;
  181.     }
  182.     return S_FALSE;
  183. }
  184. STDMETHODIMP CPJData_GetCanonicalFormatEtc(IDataObject *pdtobj, LPFORMATETC pformatetc, LPFORMATETC pformatetcOut)
  185. {
  186.     return DATA_S_SAMEFORMATETC;
  187. }
  188. STDMETHODIMP CPJData_SetData(IDataObject *pdtobj, FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
  189. {
  190.     return E_INVALIDARG;
  191. }
  192. STDMETHODIMP CPJData_EnumFormatEtc(IDataObject *pdtobj, DWORD dwDirection, LPENUMFORMATETC *ppenumFormatEtc)
  193. {
  194.     return S_FALSE;
  195. }
  196. STDMETHODIMP CPJData_Advise(IDataObject *pdtobj, FORMATETC *pFormatetc, DWORD advf, LPADVISESINK pAdvSink, DWORD *pdwConnection)
  197. {
  198.     return OLE_E_ADVISENOTSUPPORTED;
  199. }
  200. STDMETHODIMP CPJData_Unadvise(IDataObject *pdtobj, DWORD dwConnection)
  201. {
  202.     return OLE_E_ADVISENOTSUPPORTED;
  203. }
  204. STDMETHODIMP CPJData_EnumAdvise(IDataObject *pdtobj, LPENUMSTATDATA *ppenumAdvise)
  205. {
  206.     return OLE_E_ADVISENOTSUPPORTED;
  207. }
  208. const IDataObjectVtbl c_CPJDataVtbl = {
  209.     CPJData_QueryInterface, CPJData_AddRef, CPJData_Release,
  210.     CPJData_GetData,
  211.     CPJData_GetDataHere,
  212.     CPJData_QueryGetData,
  213.     CPJData_GetCanonicalFormatEtc,
  214.     CPJData_SetData,
  215.     CPJData_EnumFormatEtc,
  216.     CPJData_Advise,
  217.     CPJData_Unadvise,
  218.     CPJData_EnumAdvise
  219. };
  220. //---------------------------------------------------------------------------
  221. //
  222. // IDropTarget stuff
  223. //
  224. //=============================================================================
  225. // CPQDropTarget : class definition
  226. //=============================================================================
  227. typedef struct {        // dvdt
  228.     IDropTarget         dt;
  229.     UINT                cRef;
  230.     PQUEUEINFO          pqi;
  231.     DWORD               grfKeyStateLast;
  232.     DWORD               dwEffect;       // drop action set in DragEnter
  233.     AUTO_SCROLL_DATA    asd;            // for auto scrolling
  234. } CPQDropTarget;
  235. //=============================================================================
  236. // CPQDropTarget : Constructor
  237. //=============================================================================
  238. extern const IDropTargetVtbl c_CPQDropTarget;        // forward
  239. STDMETHODIMP CPQDropTarget_CreateInstance(PQUEUEINFO pqi, IDropTarget **ppdtgt)
  240. {
  241.     HRESULT hres = E_OUTOFMEMORY;
  242.     CPQDropTarget *pdvdt = (void*)LocalAlloc(LPTR, SIZEOF(CPQDropTarget));
  243.     if (pdvdt)
  244.     {
  245.         pdvdt->dt.lpVtbl = &c_CPQDropTarget;
  246.         pdvdt->cRef = 1;
  247.         pdvdt->pqi = pqi;
  248.         *ppdtgt = &pdvdt->dt;
  249.         hres = NOERROR;
  250.     }
  251.     return hres;
  252. }
  253. //=============================================================================
  254. // CPQDropTarget : member
  255. //=============================================================================
  256. STDMETHODIMP CPQDropTarget_QueryInterface(IDropTarget *pdt, REFIID riid, LPVOID *ppvObj)
  257. {
  258.     CPQDropTarget *this = IToClass(CPQDropTarget, dt, pdt);
  259.     // If we just want the same interface or an unknown one, return
  260.     // this guy
  261.     //
  262.     if (IsEqualIID(riid, &IID_IDropTarget) || IsEqualIID(riid, &IID_IUnknown))
  263.     {
  264.         *((IDropTarget **)ppvObj) = &this->dt;
  265.         this->cRef++;
  266.         return(NOERROR);
  267.     }
  268.     *ppvObj = NULL;
  269.     return E_NOINTERFACE;
  270. }
  271. STDMETHODIMP_(ULONG) CPQDropTarget_AddRef(IDropTarget *pdt)
  272. {
  273.     CPQDropTarget *this = IToClass(CPQDropTarget, dt, pdt);
  274.     this->cRef++;
  275.     return(this->cRef);
  276. }
  277. STDMETHODIMP_(ULONG) CPQDropTarget_Release(IDropTarget *pdt)
  278. {
  279.     CPQDropTarget *this = IToClass(CPQDropTarget, dt, pdt);
  280.     this->cRef--;
  281.     if (this->cRef > 0)
  282.         return this->cRef;
  283.     LocalFree((HLOCAL)this);
  284.     return 0;
  285. }
  286. STDMETHODIMP CPQDropTarget_DragEnter(IDropTarget *pdt, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
  287. {
  288.     CPQDropTarget *this = IToClass(CPQDropTarget, dt, pdt);
  289.     FORMATETC fmte = {g_cfPrintJob, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  290.     DebugMsg(DM_TRACE, TEXT("sh - TR CPQDropTarget::DragEnter"));
  291.     this->grfKeyStateLast = grfKeyState;
  292.     RegisterPrintJobFormat();
  293.     // default action
  294.     *pdwEffect = DROPEFFECT_NONE;
  295.     // We accept jobs from this queue:
  296.     // - the only "job" object that we accept is ours, so do a quick check
  297.     //   (this also keeps us from supporting another datatype in idataobject!)
  298.     // - there's only one outstanding drag:
  299.     //   if this pdt's queue was the source of the drag, we can accept it
  300.     if (this->pqi->bDragSource && (NOERROR == pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte)))
  301.     {
  302.         // this is a print job from our window, accept this
  303.         *pdwEffect = DROPEFFECT_MOVE;
  304.     }
  305.     else
  306.     {
  307.         FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  308.         // We also accept files for printing
  309.         if (NOERROR == pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte))
  310.         {
  311.             *pdwEffect = DROPEFFECT_COPY;
  312.         }
  313.     }
  314.     // store dwEffect for DragOver
  315.     this->dwEffect = *pdwEffect;
  316.     DAD_DragEnter(this->pqi->hwndLV);
  317.     DAD_InitScrollData(&this->asd);
  318.     return NOERROR;
  319. }
  320. int PQ_GetDropTarget(CPQDropTarget *this, long y)
  321. {
  322.     LV_HITTESTINFO info;
  323.     int iItem;
  324.     info.pt.x = 0;
  325.     info.pt.y = y;
  326.     ScreenToClient(this->pqi->hwndLV, &info.pt);
  327.     // using 10 for x forces us into "document name" column, allowing us to
  328.     // get a "hit" even if we are on some other column.
  329.     info.pt.x = 10;
  330.     iItem = ListView_HitTest(this->pqi->hwndLV, &info);
  331.     if (iItem == -1)
  332.     {
  333.         // another hack: since 100 above forces us into column 1, we know that
  334.         // a -1 return is not from being too far to the left or to the right.
  335.         // And you can't go off the top (above item 0) without falling off the
  336.         // listview.  So a -1 return means we're below the last item, so put
  337.         // us on the last item:
  338.         iItem = ListView_GetItemCount(this->pqi->hwndLV) - 1;
  339.     }
  340.     return iItem;
  341. }
  342. STDMETHODIMP CPQDropTarget_DragOver(IDropTarget *pdt, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
  343. {
  344.     HRESULT hres = NOERROR;
  345.     CPQDropTarget *this = IToClass(CPQDropTarget, dt, pdt);
  346.     POINT pt = { ptl.x, ptl.y };        // in screen coords
  347.     ScreenToClient(this->pqi->hwndLV, &pt);
  348.     // assume coords of our window match listview
  349.     DAD_AutoScroll(this->pqi->hwndLV, &this->asd, &pt);
  350.     // effect was stored in DragEnter
  351.     *pdwEffect = this->dwEffect;
  352.     // If this->dwEffect is DROPACTION_MOVE, then
  353.     // it might be nice to show where the job will move to:
  354.     //            v
  355.     //  - Use a " - " cursor to show where this job will be inserted
  356.     //            ^
  357.     DAD_DragMove(pt);
  358.     return hres;
  359. }
  360. STDMETHODIMP CPQDropTarget_DragLeave(IDropTarget *pdt)
  361. {
  362.     CPQDropTarget *this = IToClass(CPQDropTarget, dt, pdt);
  363.     DebugMsg(DM_TRACE, TEXT("sh - TR CPQDropTarget::DragLeave"));
  364.     DAD_DragLeave();
  365.     LVUtil_DragSelectItem(this->pqi->hwndLV, -1);
  366.     return NOERROR;
  367. }
  368. STDMETHODIMP CPQDropTarget_Drop(IDropTarget *pdt, IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  369. {
  370.     CPQDropTarget *this = IToClass(CPQDropTarget, dt, pdt);
  371.     HRESULT hres = NOERROR;
  372.     DebugMsg(DM_TRACE, TEXT("sh - TR CPQDropTarget::Drop"));
  373.     if (this->grfKeyStateLast & MK_LBUTTON)
  374.     {
  375.         *pdwEffect = this->dwEffect;
  376.     }
  377.     else
  378.     {
  379.         HMENU hmenu;
  380.         // pop up a menu to choose from.
  381.         if (this->dwEffect == DROPEFFECT_MOVE)
  382.         {
  383.             // we're moving a job
  384.             hmenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_MOVEONLYDD);
  385.         }
  386.         else // this->dwEffect == DROPEFFECT_COPY
  387.         {
  388.             // we're printing a file
  389.             ASSERT(this->dwEffect == DROPEFFECT_COPY);
  390.             // To use MENU_PRINTOBJ_DD we assume DDIDM_COPY == DROPEFFECT_COPY
  391.             hmenu = SHLoadPopupMenu(HINST_THISDLL, MENU_PRINTOBJ_DD);
  392.         }
  393.         if (hmenu)
  394.         {
  395.             SetMenuDefaultItem(hmenu, this->dwEffect, MF_BYCOMMAND);
  396.             *pdwEffect = TrackPopupMenu(
  397.                     hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  398.                     ptl.x, ptl.y, 0, this->pqi->hwndLV, NULL);
  399.             DestroyMenu(hmenu);
  400.             DebugMsg(DM_TRACE, TEXT("CPQDropTarget_Drop *pdwEffect = %x, this->dwEffect=%x"), *pdwEffect, this->dwEffect);
  401.         }
  402.     }
  403.     // Do the drop!
  404.     if (*pdwEffect == DROPEFFECT_COPY)
  405.     {
  406.         // we're printing the file pdtobj to printer idp
  407.         hres = PrintObj_DropPrint(pdtobj, this->pqi->hDlg, *pdwEffect, (LPCITEMIDLIST)&(this->pqi->idp), CPrintObj_DropThreadProc);
  408.     }
  409.     else if (*pdwEffect == DROPEFFECT_MOVE)
  410.     {
  411.         STGMEDIUM medium;
  412.         FORMATETC fmte = {g_cfPrintJob, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  413.         if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium)))
  414.         {
  415.             int iItem;
  416.             BOOL bRet;
  417.             PRINTJOBDESC *pidpj = (PRINTJOBDESC *)GlobalLock(medium.hGlobal);
  418.             ASSERT(pidpj->dwJobId == this->pqi->pJobs[pidpj->iItem].JobId);
  419.             iItem = PQ_GetDropTarget(this, ptl.y);
  420.             DebugMsg(DM_TRACE, TEXT("Drop %d on %d"), pidpj->iItem, iItem);
  421.             bRet = FALSE;
  422.             this->pqi->pJobs[pidpj->iItem].Position = iItem + 1;
  423.             bRet = SetJob(this->pqi->hPrinter, pidpj->dwJobId, 2,
  424.                         (LPBYTE)&(this->pqi->pJobs[pidpj->iItem]), 0);
  425.             if (bRet)
  426.             {
  427.                 PostMessage(this->pqi->hDlg, WM_TIMER, 1, 0L);
  428.             }
  429.             else
  430.             {
  431.                 MessageBeep((UINT)-1);
  432.             }
  433.             GlobalUnlock(medium.hGlobal);
  434.             ReleaseStgMedium(&medium);
  435.         }
  436.     }
  437.     // Make sure to unlock the window for updating
  438.     CPQDropTarget_DragLeave(pdt);
  439.     return hres;
  440. }
  441. const IDropTargetVtbl c_CPQDropTarget = {
  442.     CPQDropTarget_QueryInterface, CPQDropTarget_AddRef, CPQDropTarget_Release,
  443.     CPQDropTarget_DragEnter,
  444.     CPQDropTarget_DragOver,
  445.     CPQDropTarget_DragLeave,
  446.     CPQDropTarget_Drop
  447. };
  448. //---------------------------------------------------------------------------
  449. //
  450. // More D&D functions
  451. //
  452. LRESULT PRQ_BeginDrag(HWND hDlg, PQUEUEINFO pqi, NM_LISTVIEW *lpnm)
  453. {
  454.     int iJob = ListView_GetNextItem(pqi->hwndLV, -1, LVNI_SELECTED);
  455.     LPITEMIDLIST pidlParent;
  456.     ASSERT(iJob >= 0);
  457.     pidlParent = Printers_GetPidl(NULL, pqi->idp.cName);
  458.     if (pidlParent)
  459.     {
  460.         POINT ptOffset = lpnm->ptAction;             // hwndLV client coords
  461.         DWORD dwEffect = DROPEFFECT_MOVE;
  462.         PRINTJOBDESC pjd;
  463.         BOOL fPaused;
  464.         // set up relative pidl for job
  465.         pjd.dwJobId = pqi->pJobs[iJob].JobId;
  466.         pjd.iItem = iJob;
  467.         // pause the job so it won't start printing during drag operation
  468.         fPaused = !(pqi->pJobs[iJob].Status & JOB_STATUS_PAUSED) &&
  469.                 Printer_ModifyJob(hDlg, JOB_CONTROL_PAUSE);
  470.         // Somebody began dragging in our window, so store that fact
  471.         // save away the anchor point
  472.         // pqi->ptDragAnchor = lpnm->ptAction;
  473.         // LVUtil_ClientToLV(pqi->hwndLV, &pqi->ptDragAnchor);
  474.         ClientToScreen(pqi->hwndLV, &ptOffset);     // now in screen
  475.         if (DAD_SetDragImageFromListView(pqi->hwndLV, ptOffset))
  476.         {
  477.             IDataObject *pdtobj;
  478.             if (SUCCEEDED(CPJData_CreateInstance(&pjd, &pdtobj)))
  479.             {
  480.                 pqi->bDragSource = TRUE;
  481.                 SHDoDragDrop(hDlg, pdtobj, NULL, dwEffect, &dwEffect);
  482.                 pdtobj->lpVtbl->Release(pdtobj);
  483.                 pqi->bDragSource = FALSE;
  484.             }
  485.             DAD_SetDragImage(NULL, NULL);
  486.         }
  487.         // All done dragging
  488.         if (fPaused)
  489.         {
  490.             Printer_ModifyJob(hDlg, JOB_CONTROL_RESUME);
  491.         }
  492.         ILFree(pidlParent);
  493.     }
  494.     return 0L;
  495. }
  496. /********************************************************************/
  497. /* Printer Queue helper functions                                   */
  498. /********************************************************************/
  499. void SystemTimeString(LPTSTR pszText, SYSTEMTIME *pst)
  500. {
  501.     FILETIME ftUTC, ftLocal;
  502.     SYSTEMTIME st;
  503.     TCHAR szTmp[64];
  504.     // Convert from UTC to Local time
  505.     SystemTimeToFileTime(pst, &ftUTC);
  506.     FileTimeToLocalFileTime(&ftUTC, &ftLocal);
  507.     FileTimeToSystemTime(&ftLocal, &st);
  508.     // Generate string
  509.     GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, pszText, ARRAYSIZE(szTmp));
  510.     lstrcat(pszText, c_szSpace);
  511.     GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, szTmp, ARRAYSIZE(szTmp));
  512.     lstrcat(pszText, szTmp);
  513. }
  514. const struct
  515. {
  516.     int iCol;
  517.     int ids;    // Id of string for title
  518.     int iFmt;   // The format of the column;
  519. } c_PQCols[] = {
  520.     {ICOL_DOCNAME , IDS_PRQ_DOCNAME , LVCFMT_LEFT},
  521.     {ICOL_STATUS  , IDS_PRQ_STATUS  , LVCFMT_LEFT},
  522.     {ICOL_OWNER   , IDS_PRQ_OWNER   , LVCFMT_LEFT},
  523.     {ICOL_PROGRESS, IDS_PRQ_PROGRESS, LVCFMT_LEFT},
  524.     {ICOL_TIME    , IDS_PRQ_TIME    , LVCFMT_LEFT},
  525. };
  526. void PrinterQueue_AddColumns(HWND hLV, int *nPos)
  527. {
  528.     LV_COLUMN col;
  529.     TCHAR szColName[258];
  530.     int i;
  531.     for (i=0; i<ARRAYSIZE(c_PQCols); ++i)
  532.     {
  533.         LoadString(HINST_THISDLL, c_PQCols[i].ids, szColName, ARRAYSIZE(szColName));
  534.         col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  535.         col.fmt  = c_PQCols[i].iFmt;
  536.         col.pszText = (LPTSTR)szColName;
  537.         col.cchTextMax = 0;
  538.         col.cx = nPos[i];
  539.         col.iSubItem = c_PQCols[i].iCol;
  540.         ListView_InsertColumn(hLV, c_PQCols[i].iCol, &col);
  541.     }
  542. }
  543. const TCHAR szPrinterPositions[] = REGSTR_PATH_EXPLORER TEXT("\Printers") ;
  544. const POSINFO c_PQPos = {
  545.     SIZEOF(WINDOWPLACEMENT), 0, SW_SHOW, 0, 0, 0, 0, 50, 100, 612, 285,
  546.     185, 80, 80, 80, 125, TRUE
  547. };
  548. BOOL PrinterQueue_Init(HWND hDlg, INITINFO *pii)
  549. {
  550.     POSINFO sPos;
  551.     HWND hLV;
  552.     PQUEUEINFO pqi;
  553.     IDropTarget *lpdt;
  554.     HKEY hkey;
  555.     HANDLE hPrinter;
  556.     HWND hSB;
  557.     HIMAGELIST himl;
  558.     int border[] = {0, -1, 2};
  559.     // Let the printer hwnd dpa point to this hDlg instead of stub parent.
  560.     PrintDef_UpdateHwnd(pii->lpszPrinterName, hDlg);
  561.     hLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, c_szNULL,
  562.         WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_CLIPSIBLINGS|LVS_REPORT,
  563.         0, 0, 100, 100, hDlg, NULL, HINST_THISDLL, NULL);
  564.     if (!hLV)
  565.     {
  566.         goto Error1;
  567.     }
  568.     hSB = CreateStatusWindow(
  569.         WS_CHILD | SBARS_SIZEGRIP | CCS_NOHILITE | WS_CLIPSIBLINGS,
  570.         NULL, hDlg, FCIDM_STATUS);
  571.     if (!hSB)
  572.     {
  573.         goto Error1;
  574.     }
  575.     SendMessage(hSB, SB_SETBORDERS, 0, (LPARAM)(LPINT)border);
  576.     himl = ImageList_Create(g_cxSmIcon, g_cySmIcon, ILC_MASK, 1, 4);
  577.     if (himl)
  578.     {
  579.         HICON hIcon;
  580.         int iIndex;
  581.         ImageList_SetBkColor(himl, GetSysColor(COLOR_WINDOW));
  582.         hIcon = LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_DOCUMENT),
  583.                         IMAGE_ICON, g_cxSmIcon, g_cySmIcon, LR_DEFAULTCOLOR);
  584.         iIndex = ImageList_AddIcon(himl, hIcon);
  585.         ASSERT(iIndex == 0);
  586.         DestroyIcon(hIcon);
  587.         SendMessage(hLV, LVM_SETIMAGELIST, (WPARAM)LVSIL_SMALL, (LPARAM)himl);
  588.     }
  589.     hPrinter = Printer_OpenPrinter(pii->lpszPrinterName);
  590.     if (!hPrinter)
  591.     {
  592.         goto Error1;
  593.     }
  594.     pqi = (PQUEUEINFO)LocalAlloc(LPTR, SIZEOF(QUEUEINFO));
  595.     if (!pqi)
  596.     {
  597.         Printer_ClosePrinter(hPrinter);
  598. Error1:
  599.         PostQuitMessage(0);
  600.         return(TRUE);
  601.     }
  602.     pqi->hPrinter = hPrinter;
  603.     pqi->hDlg = hDlg;
  604.     pqi->hwndLV = hLV;
  605.     Printers_FillPidl(&pqi->idp, pii->lpszPrinterName);
  606.     SetWindowPtr(hDlg, DWLP_USER, pqi);
  607.     sPos = c_PQPos;
  608.     if (ERROR_SUCCESS ==
  609.         RegOpenKey(HKEY_CURRENT_USER, szPrinterPositions, &hkey))
  610.     {
  611.         DWORD dwSize = SIZEOF(sPos);
  612.         DWORD dwType;
  613.         if (ERROR_SUCCESS !=
  614.             SHQueryValueEx(hkey, (LPTSTR)(pii->lpszPrinterName), NULL, &dwType, (LPBYTE)&sPos, &dwSize)
  615.             || dwSize != SIZEOF(sPos)
  616.             || sPos.wp.length != SIZEOF(WINDOWPLACEMENT))
  617.         {
  618.             sPos = c_PQPos;
  619.         }
  620.         RegCloseKey(hkey);
  621.     }
  622.     pqi->hwndSB = hSB;
  623.     pqi->fStatusBar = sPos.fStatusBar;
  624.     ShowWindow(pqi->hwndSB, pqi->fStatusBar ? SW_SHOW : SW_HIDE);
  625.     PrinterQueue_AddColumns(hLV, sPos.nWidths);
  626.     Printer_LoadIcons(pqi->idp.cName, &(pqi->hLargeIcon), &(pqi->hSmallIcon));
  627.     SendMessage(pqi->hDlg, WM_SETICON, TRUE, (LPARAM)(pqi->hLargeIcon));
  628.     SendMessage(pqi->hDlg, WM_SETICON, FALSE, (LPARAM)(pqi->hSmallIcon));
  629.     PostMessage(hDlg, WM_TIMER, 1, 0L);
  630.     sPos.wp.showCmd = pii->nCmdShow;
  631.     SetWindowPlacement(hDlg, &sPos.wp);
  632.     if (SUCCEEDED(CPQDropTarget_CreateInstance(pqi, &lpdt)))
  633.     {
  634.         RegisterDragDrop(hLV, lpdt);
  635.     }
  636.     return(TRUE);
  637. }
  638. void PrinterQueue_Destroy(HWND hDlg)
  639. {
  640.     PQUEUEINFO pqi;
  641.     POSINFO sPos;
  642.     int i;
  643.     HKEY hkey;
  644.     pqi = (PQUEUEINFO)GetWindowPtr(hDlg, DWLP_USER);
  645.     if (!pqi)
  646.     {
  647.         // Nothing was initialized, so nothing to clean up
  648.         return;
  649.     }
  650.     SendMessage(pqi->hDlg, WM_SETICON, TRUE, 0);
  651.     SendMessage(pqi->hDlg, WM_SETICON, FALSE, 0);
  652.     DestroyIcon(pqi->hLargeIcon);
  653.     DestroyIcon(pqi->hSmallIcon);
  654.     RevokeDragDrop(pqi->hwndLV);
  655.     sPos.wp.length = SIZEOF(WINDOWPLACEMENT);
  656.     GetWindowPlacement(hDlg, &sPos.wp);
  657.     // Get the column widths
  658.     for (i=0; i<ICOL_NUM; ++i)
  659.     {
  660.         sPos.nWidths[i] = ListView_GetColumnWidth(pqi->hwndLV, i);
  661.     }
  662.     sPos.fStatusBar = pqi->fStatusBar;
  663.     // Save the position info
  664.     if (ERROR_SUCCESS ==
  665.         RegCreateKey(HKEY_CURRENT_USER, szPrinterPositions, &hkey))
  666.     {
  667.         RegSetValueEx(hkey, pqi->idp.cName, 0, REG_BINARY,
  668.                 (LPBYTE)&sPos, SIZEOF(sPos));
  669.         RegCloseKey(hkey);
  670.     }
  671.     if (pqi->hPrinter)
  672.     {
  673.         Printer_ClosePrinter(pqi->hPrinter);
  674.     }
  675.     if (pqi->pJobs)
  676.     {
  677.         LocalFree((HLOCAL)pqi->pJobs);
  678.     }
  679.     LocalFree(pqi);
  680.     SetWindowPtr(hDlg, DWLP_USER, 0L);
  681. }
  682. #endif
  683. // Printer_BitsToString maps bits into a string representation, putting
  684. // the string idsSep in between each found bit.
  685. // Returns the size of the created string.
  686. UINT Printer_BitsToString(
  687.     DWORD          bits,       // the bitfield we're looking at
  688.     UINT           idsSep,     // string id of separator
  689.     LPCSTATUSSTUFF pSS,        // a 0 terminated mapping of bits to string ids
  690.     LPTSTR         lpszBuf,    // output buffer
  691.     UINT           cchMax)     // size of output buffer
  692. {
  693.     UINT cchBuf = 0;
  694.     UINT i = 0;
  695.     UINT cchSep = 0;
  696.     TCHAR szSep[20];
  697.     if (LoadString(HINST_THISDLL, idsSep, szSep, ARRAYSIZE(szSep)))
  698.         cchSep = lstrlen(szSep);
  699.     for ( ; pSS->bit != 0 ; ++i, ++pSS)
  700.     {
  701.         if (bits & pSS->bit)
  702.         {
  703.             TCHAR szTmp[258];
  704.             if (LoadString(HINST_THISDLL, pSS->uStringID, szTmp, ARRAYSIZE(szTmp)))
  705.             {
  706.                 UINT cchTmp = lstrlen(szTmp);
  707.                 if (cchBuf + cchSep + cchTmp < cchMax)
  708.                 {
  709.                     if (cchBuf)
  710.                     {
  711.                         lstrcat(lpszBuf, szSep);
  712.                         cchBuf += cchSep;
  713.                     }
  714.                     lstrcat(lpszBuf, szTmp);
  715.                     cchBuf += cchTmp;
  716.                 }
  717.             }
  718.         }
  719.     }
  720.     return(cchBuf);
  721. }
  722. #ifndef WINNT
  723. extern const STATUSSTUFF ssPrinterStatus[]; // defined in printer.c
  724. void Printer_PrinterStatus(PQUEUEINFO pqi)
  725. {
  726.     LPPRINTER_INFO_2 pPrinter;
  727.     LPTSTR  lpszPrinterName;
  728.     TCHAR   szBuf[256];
  729.     // Default window title is the current printer name
  730.     lpszPrinterName = pqi->idp.cName;
  731.     pPrinter = Printer_GetPrinterInfo(pqi->hPrinter, 2);
  732.     if (pPrinter)
  733.     {
  734.         UINT bufLen;
  735.         UINT nameLen;
  736.         if (lstrcmp(pqi->idp.cName, pPrinter->pPrinterName))
  737.         {
  738.             // WHOA! The user renamed this printer
  739.             Printers_FillPidl(&pqi->idp, pPrinter->pPrinterName);
  740.             // BUGBUG: We don't update the hdsaPrintDef (which keeps track
  741.             // of which printers are open) with the new printer name.  So
  742.             // the user can open another window for this printer OR rename a
  743.             // different printer to this one's old name and then get this
  744.             // window when trying to open the other printer.
  745.         }
  746.         if (pPrinter->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE)
  747.         {
  748.             // HACK: Use this free bit for "Work Offline"
  749.             pPrinter->Status |= PRINTER_HACK_WORK_OFFLINE;
  750.         }
  751.         if (pPrinter->Status)
  752.         {
  753.             lstrcpy(szBuf, lpszPrinterName);
  754.             nameLen = bufLen = lstrlen(szBuf);
  755.             LoadString(HINST_THISDLL, IDS_PRQSTATUS_SEPARATOR,
  756.                 szBuf+bufLen, ARRAYSIZE(szBuf)-bufLen);
  757.             bufLen = lstrlen(szBuf);
  758.             Printer_BitsToString(pPrinter->Status, IDS_PRQSTATUS_SEPARATOR,
  759.                 ssPrinterStatus, szBuf+bufLen, ARRAYSIZE(szBuf)-bufLen);
  760.             lpszPrinterName = szBuf;
  761.         }
  762.         LocalFree((HLOCAL)pPrinter);
  763.     }
  764.     SetWindowText(pqi->hDlg, lpszPrinterName);
  765. }
  766. // Printer_AddJobs sets the number of items in hLV to be iJobsWanted.
  767. void Printer_AddJobs(HWND hLV, int iJobsWanted)
  768. {
  769.     LV_ITEM item;
  770.     int iJobs;
  771.     // And to the listview
  772.     item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE ;
  773.     item.iSubItem = 0;
  774.     item.pszText = LPSTR_TEXTCALLBACK;
  775.     item.state = 0;
  776.     item.iImage = -1;
  777.     iJobs = ListView_GetItemCount(hLV);
  778.     for ( ; iJobs<iJobsWanted; ++iJobs)
  779.     {
  780.         item.iItem = iJobs;
  781.         item.lParam = (LPARAM)iJobs;
  782.         if (ListView_InsertItem(hLV, &item) < 0)
  783.         {
  784.             break;
  785.         }
  786.     }
  787.     for ( ; iJobs>iJobsWanted; --iJobs)
  788.     {
  789.         ListView_DeleteItem(hLV, iJobs-1);
  790.     }
  791. }
  792. BOOL Printers_EnumJobsCB(LPVOID lpData, HANDLE hPrinter, DWORD dwLevel,
  793.     LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
  794. {
  795. #ifndef BUGBUG_BOBDAY
  796.     return(EnumJobs(hPrinter, 0, 0x7fffffffL, dwLevel, pEnum, dwSize, lpdwNeeded, lpdwNum));
  797. #else
  798.     return(EnumJobs(hPrinter, 1, 0x7fffffffL, dwLevel, pEnum, dwSize, lpdwNeeded, lpdwNum));
  799. #endif
  800. }
  801. void PrinterQueue_Check(HWND hDlg)
  802. {
  803.     PQUEUEINFO pqi = (PQUEUEINFO)GetWindowPtr(hDlg, DWLP_USER);
  804.     DWORD dwJobs;
  805.     int iJob, iSel;
  806.     DWORD dwSelID;
  807.     LPTSTR pszJobsInQueue;
  808.     ASSERT(hDlg == pqi->hDlg);
  809.     if (pqi->bDragSource || pqi->bMinimized)
  810.     {
  811.         // If we are dragging from this window, don't update the queue info!
  812.         // If we did, we'd need to put semaphores around this function and
  813.         // all the drag&drop stuff.  This is much easier.
  814.         // If we are minimized, we don't need to poll either
  815.         return;
  816.     }
  817.     // remember which job is selected
  818.     dwSelID = (DWORD)-1;
  819.     iSel = ListView_GetNextItem(pqi->hwndLV, -1, LVNI_SELECTED);
  820.     if (iSel >= 0)
  821.     {
  822.         dwSelID = pqi->pJobs[iSel].JobId;
  823.     }
  824.     // update title
  825.     Printer_PrinterStatus(pqi);
  826.     // get new job state
  827.     if (pqi->pJobs)
  828.         LocalFree((HLOCAL)pqi->pJobs);
  829.     pqi->pJobs = Printer_EnumProps(pqi->hPrinter, 2, &dwJobs, Printers_EnumJobsCB, NULL);
  830.     if (pqi->pJobs == NULL)
  831.     {
  832.         pqi->dwJobs = dwJobs = 0;
  833.     }
  834.     // Make sure we set pqi->dwJobs before we call anything that might
  835.     // recurse back into PrintQueue_DlgProc where we might fault!
  836.     pqi->dwJobs = dwJobs;
  837.     // Make sure we set pqi->dwJobs before we call anything that might
  838.     // recurse back into PrintQueue_DlgProc where we might fault!
  839.     pqi->dwJobs = dwJobs;
  840.     pszJobsInQueue = ShellConstructMessageString(HINST_THISDLL,
  841.                         MAKEINTRESOURCE(IDS_PRQ_JOBSINQUEUE), dwJobs);
  842.     if (pszJobsInQueue)
  843.     {
  844.         // a-msadek; needed only for BiDi Win95 loc
  845.         // Mirroring will take care of that over NT5 & BiDi Win98
  846.         if(g_bBiDiW95Loc)
  847.         {
  848.             SendMessage(pqi->hwndSB, SB_SETTEXT, (WPARAM)SBT_RTLREADING, (LPARAM)pszJobsInQueue);
  849.         }
  850.         else
  851.         {
  852.             SendMessage(pqi->hwndSB, SB_SETTEXT, (WPARAM)0, (LPARAM)pszJobsInQueue);
  853.         }    
  854.         LocalFree(pszJobsInQueue);
  855.     }
  856.     Printer_AddJobs(pqi->hwndLV, (int)dwJobs);
  857.     // Find the previously selected job and select it again
  858.     iSel = -1;
  859.     for (iJob = 0; iJob < (int)dwJobs; iJob++)
  860.     {
  861.         if (pqi->pJobs[iJob].JobId == dwSelID)
  862.         {
  863.             iSel = iJob;
  864.         }
  865.     }
  866.     ListView_SetItemState(pqi->hwndLV, iSel, iSel == -1 ? 0 : LVIS_SELECTED, LVIS_SELECTED);
  867.     InvalidateRect(pqi->hwndLV, NULL, TRUE);
  868. }
  869. BOOL Printer_ModifyJob(HWND hDlg, DWORD dwCommand)
  870. {
  871.     PQUEUEINFO pqi = (PQUEUEINFO)GetWindowPtr(hDlg, DWLP_USER);
  872.     int iJob = -1;
  873.     DWORD dwJobID;
  874.     BOOL bRet = TRUE;
  875.     while (bRet &&
  876.            (iJob = ListView_GetNextItem(pqi->hwndLV, iJob, LVNI_SELECTED)) >= 0)
  877.     {
  878.         dwJobID = pqi->pJobs[iJob].JobId;
  879.         bRet = SetJob(pqi->hPrinter, dwJobID, 0, NULL, dwCommand);
  880.     }
  881.     // Let the listbox refresh right now
  882.     PostMessage(hDlg, WM_TIMER, 1, 0L);
  883.     return(bRet);
  884. }
  885. #if 0 // not used
  886. BOOL PrinterDlg_ModifyPrinter(PQUEUEINFO pqi, DWORD dwCommand)
  887. {
  888.     BOOL fRet = SetPrinter(pqi->hPrinter, 0, NULL, dwCommand);
  889.     // Let the listbox refresh right now
  890.     PostMessage(pqi->hDlg, WM_TIMER, 1, 0L);
  891.     return fRet;
  892. }
  893. #endif
  894. void PrinterQueue_InitPrinterMenu(HMENU hmPrinter, PQUEUEINFO pqi)
  895. {
  896.     QCMINFO qcm = {hmPrinter, 0, ID_PRINTER_START, 0xffff};
  897.     int i;
  898.     // remove previously merged items
  899.     for (i = GetMenuItemCount(hmPrinter) ; i > 3 ; i--)
  900.     {
  901.         DeleteMenu(hmPrinter, 0, MF_BYPOSITION);
  902.     }
  903.     // merge in context menu
  904.     Printer_MergeMenu(NULL, &qcm, pqi->idp.cName, TRUE);
  905.     // remove Open
  906.     DeleteMenu(hmPrinter, 0, MF_BYPOSITION);
  907.     DeleteMenu(hmPrinter, 0, MF_BYPOSITION);
  908. }
  909. void PrinterQueue_InitDocMenu(HMENU hmDoc, PQUEUEINFO pqi, int iSel)
  910. {
  911.     UINT i = (iSel >= 0 && pqi->pJobs[iSel].Status & JOB_STATUS_PAUSED) ?
  912.                 MF_CHECKED : 0 ;
  913.     Printer_CheckMenuItem(hmDoc, i, ID_DOCUMENT_RESUME, ID_DOCUMENT_PAUSE);
  914.     Printer_EnableMenuItems(hmDoc, iSel >= 0);
  915. }
  916. void PrinterQueue_InitViewMenu(HMENU hmView, PQUEUEINFO pqi)
  917. {
  918.     // This seems a bit bogus to me.  We can simply un/check the menu item
  919.     // whenever the user makes the change.  That won't work for the
  920.     // Printer and Document menu's, since they may change asynchronous to
  921.     // user input.  To be consistent, let's follow the same model here.
  922.     CheckMenuItem(hmView, ID_VIEW_STATUSBAR,
  923.         pqi->fStatusBar ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  924. }
  925. //
  926. // load a popup from a main menu
  927. //
  928. HMENU _LoadSubPopupMenu(UINT id, UINT uSubOffset)
  929. {
  930.     HMENU hmParent, hmPopup;
  931.     hmParent = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(id));
  932.     if (!hmParent)
  933.         return NULL;
  934.     hmPopup = GetSubMenu(hmParent, uSubOffset);
  935.     RemoveMenu(hmParent, uSubOffset, MF_BYPOSITION);
  936.     DestroyMenu(hmParent);
  937.     return hmPopup;
  938. }
  939. const STATUSSTUFF ssPRQStatus[] =
  940. {
  941.     JOB_STATUS_DELETING, IDS_PRQSTATUS_PENDING_DELETION,
  942.     JOB_STATUS_ERROR   , IDS_PRQSTATUS_ERROR   ,
  943.     JOB_STATUS_OFFLINE , IDS_PRQSTATUS_OFFLINE ,
  944.     JOB_STATUS_PAPEROUT, IDS_PRQSTATUS_PAPER_OUT,
  945.     JOB_STATUS_PAUSED  , IDS_PRQSTATUS_PAUSED  ,
  946.     JOB_STATUS_PRINTED , IDS_PRQSTATUS_PRINTED ,
  947.     JOB_STATUS_PRINTING, IDS_PRQSTATUS_PRINTING,
  948.     JOB_STATUS_SPOOLING, IDS_PRQSTATUS_SPOOLING,
  949.     JOB_STATUS_USER_INTERVENTION, IDS_PRQSTATUS_USER_INTERVENTION,
  950.     0, 0
  951. } ;
  952. void PrintQueue_Notify(HWND hDlg, NMHDR *lphdr)
  953. {
  954.     PQUEUEINFO pqi = (PQUEUEINFO)GetWindowPtr(hDlg, DWLP_USER);
  955.     if (!pqi)
  956.     {
  957.         return;
  958.     }
  959.     switch (lphdr->code)
  960.     {
  961.     case NM_RCLICK:
  962.     {
  963.         int iSel;
  964.         HMENU hmContext;
  965.         POINT pt;
  966.         iSel = ListView_GetNextItem(pqi->hwndLV, -1, LVNI_SELECTED);
  967.         hmContext = _LoadSubPopupMenu(MENU_PRINTERQUEUE, iSel >= 0 ? 1 : 0);
  968.         if (!hmContext)
  969.         {
  970.             break;
  971.         }
  972.         if (iSel < 0)
  973.         {
  974.             // We need to remove the "Close" menu item
  975.             // (and separator)
  976.             iSel = GetMenuItemCount(hmContext) - 2;
  977.             DeleteMenu(hmContext, iSel, MF_BYPOSITION);
  978.             DeleteMenu(hmContext, iSel, MF_BYPOSITION);
  979.             PrinterQueue_InitPrinterMenu(hmContext, pqi);
  980.         }
  981.         else
  982.         {
  983.             PrinterQueue_InitDocMenu(hmContext, pqi, iSel);
  984.         }
  985.         GetMsgPos(&pt);
  986.         // The command will just get stuck in the regular queue and
  987.         // handled at that time
  988.         TrackPopupMenu(hmContext, TPM_LEFTALIGN|TPM_RIGHTBUTTON, pt.x, pt.y,
  989.             0, hDlg, NULL);
  990.         DestroyMenu(hmContext);
  991.         // Tell the listview that we handled the context menu
  992.         // so don't try to display some generic ctx menu for us
  993.         SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
  994.         break;
  995.     }
  996.     case LVN_GETDISPINFO:
  997.     {
  998.         LV_DISPINFO *lpdi = (LV_DISPINFO *)lphdr;
  999.         JOB_INFO_2 *pJob;
  1000.         if (lpdi->item.mask & LVIF_IMAGE)
  1001.         {
  1002.             lpdi->item.iImage = 0;
  1003.         }
  1004.         if (!(lpdi->item.mask & LVIF_TEXT))
  1005.         {
  1006.             // we only have info for LVIF_TEXT
  1007.             break;
  1008.         }
  1009.         // Why is this using lParam? iItem tells us the selection...
  1010.         // Stress page faulted referencing pJob+lpdi->item.lParam.
  1011.         // Shouldn't have happened, so put these asserts here and
  1012.         // bail if lParam is bad.
  1013.         ASSERT(lpdi->item.lParam == lpdi->item.iItem);
  1014.         pJob = pqi->pJobs;
  1015.         if (!pJob || (DWORD)lpdi->item.lParam >= pqi->dwJobs)
  1016.         {
  1017.             return;
  1018.         }
  1019.         pJob += lpdi->item.lParam;
  1020.         switch (lpdi->item.iSubItem)
  1021.         {
  1022.         case ICOL_DOCNAME:
  1023.             if (pJob->pDocument)
  1024.                 lpdi->item.pszText = pJob->pDocument;
  1025.             break;
  1026.         case ICOL_STATUS:
  1027.             if (pJob->pStatus && *(pJob->pStatus))
  1028.             {
  1029.                 lstrcpyn(lpdi->item.pszText, pJob->pStatus, lpdi->item.cchTextMax);
  1030.             }
  1031.             else
  1032.             {
  1033.                 Printer_BitsToString(pJob->Status, IDS_PRQSTATUS_SEPARATOR,
  1034.                     ssPRQStatus, lpdi->item.pszText, lpdi->item.cchTextMax);
  1035.             }
  1036.             break;
  1037.         case ICOL_OWNER:
  1038.             if (pJob->pUserName)
  1039.                 lpdi->item.pszText = pJob->pUserName;
  1040.             break;
  1041.         case ICOL_PROGRESS:
  1042.         {
  1043.             LPTSTR pszFormat = NULL;
  1044.             // If TotalPages == 0 and Size != 0, PagesPrinted is SizePrinted
  1045.             if (pJob->Status&JOB_STATUS_PRINTING)
  1046.             {
  1047.                 if (pJob->TotalPages == 0)
  1048.                 {
  1049.                     TCHAR szSize[32];
  1050.                     TCHAR szPrinted[32];
  1051.                     ShortSizeFormat(pJob->Size, szSize);
  1052.                     ShortSizeFormat(pJob->PagesPrinted, szPrinted);
  1053.                     pszFormat = ShellConstructMessageString(HINST_THISDLL,
  1054.                                     MAKEINTRESOURCE(IDS_PRQ_BYTESPRINTED),
  1055.                                     szPrinted, szSize);
  1056.                 }
  1057.                 else
  1058.                 {
  1059.                     pszFormat = ShellConstructMessageString(HINST_THISDLL,
  1060.                                     MAKEINTRESOURCE(IDS_PRQ_PAGESPRINTED),
  1061.                                     pJob->PagesPrinted, pJob->TotalPages);
  1062.                 }
  1063.             }
  1064.             else
  1065.             {
  1066.                 if (pJob->TotalPages == 0)
  1067.                 {
  1068.                     ShortSizeFormat(pJob->Size, lpdi->item.pszText);
  1069.                 }
  1070.                 else
  1071.                 {
  1072.                     pszFormat = ShellConstructMessageString(HINST_THISDLL,
  1073.                                     MAKEINTRESOURCE(IDS_PRQ_PAGES),
  1074.                                     pJob->TotalPages);
  1075.                 }
  1076.             }
  1077.             if (pszFormat)
  1078.             {
  1079.                 lstrcpyn(lpdi->item.pszText, pszFormat, lpdi->item.cchTextMax);
  1080.                 LocalFree(pszFormat);
  1081.             }
  1082.             break;
  1083.         }
  1084.         case ICOL_TIME:
  1085.             SystemTimeString(lpdi->item.pszText, &pJob->Submitted);
  1086.             break;
  1087.         default:
  1088.             break;
  1089.         }
  1090.         break;
  1091.     }
  1092.     case LVN_BEGINDRAG:
  1093.     case LVN_BEGINRDRAG:
  1094.         PRQ_BeginDrag(hDlg, pqi, (NM_LISTVIEW *)lphdr);
  1095.         break;
  1096.     default:
  1097.         break;
  1098.     }
  1099. }
  1100. void PrinterQueue_OnSize(PQUEUEINFO pqi)
  1101. {
  1102.     int dyList;
  1103.     int dyStatus = 0;
  1104.     if (pqi->fStatusBar)
  1105.     {
  1106.         RECT rc;
  1107.         //
  1108.         // REVIEW: Is this the right way to do this?
  1109.         //
  1110.         GetWindowRect(pqi->hwndSB, &rc);
  1111.         dyStatus = min(pqi->cy, rc.bottom - rc.top - 2);
  1112.     }
  1113.     dyList = pqi->cy - dyStatus;
  1114.     SetWindowPos(pqi->hwndLV, NULL, 0, 0, pqi->cx, dyList, SWP_NOZORDER);
  1115.     SetWindowPos(pqi->hwndSB, NULL, dyList, 0, pqi->cx, dyStatus, SWP_NOZORDER);
  1116. }
  1117. extern TCHAR const c_szWindowsHlp[];
  1118. BOOL CALLBACK PrinterQueue_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1119. {
  1120.     PQUEUEINFO pqi = (PQUEUEINFO)GetWindowPtr(hDlg, DWLP_USER);
  1121.     INSTRUMENT_WNDPROC(SHCNFI_PRINTERQUEUE_DLGPROC, hDlg, uMsg, wParam, lParam);
  1122.     switch (uMsg)
  1123.     {
  1124.     case WM_INITDIALOG:
  1125.         return(PrinterQueue_Init(hDlg, (INITINFO *)lParam));
  1126.     case WM_DESTROY:
  1127.         PrinterQueue_Destroy(hDlg);
  1128.         break;
  1129.     case WM_SIZE:
  1130.         if (wParam == SIZE_MINIMIZED)
  1131.         {
  1132.             pqi->bMinimized = TRUE;
  1133.         }
  1134.         else
  1135.         {
  1136.             if (pqi->bMinimized)
  1137.             {
  1138.                 pqi->bMinimized = FALSE;
  1139.                 // Restoring from a minimized state -- update queue
  1140.                 PostMessage(hDlg, WM_TIMER, 1, 0L);
  1141.             }
  1142.             pqi->cx = GET_X_LPARAM(lParam);
  1143.             pqi->cy = GET_Y_LPARAM(lParam);
  1144.             PrinterQueue_OnSize(pqi);
  1145.         }
  1146.         break;
  1147.     case WM_CONTEXTMENU:
  1148.         if (hDlg == (HWND)wParam &&
  1149.             SendMessage(hDlg, WM_NCHITTEST, 0, lParam) == HTSYSMENU)
  1150.         {
  1151.             HMENU hmContext = _LoadSubPopupMenu(MENU_PRINTERQUEUE, 0);
  1152.             if (hmContext)
  1153.             {
  1154.                 int idCmd;
  1155.                 PrinterQueue_InitPrinterMenu(hmContext, pqi);
  1156.                 idCmd = TrackPopupMenu(hmContext,
  1157.                         TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  1158.                         GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hDlg, NULL);
  1159.                 switch(idCmd)
  1160.                 {
  1161.                 case 0:
  1162.                     break;
  1163.                 case IDCANCEL:
  1164.                     PostQuitMessage(0);
  1165.                     break;
  1166.                 default:
  1167.                     Printer_InvokeCommand(hDlg, NULL, &(pqi->idp), idCmd-ID_PRINTER_START, 0, NULL);
  1168.                     break;
  1169.                 }
  1170.                 DestroyMenu(hmContext);
  1171.                 return TRUE;
  1172.             }
  1173.         }
  1174.         return FALSE;
  1175.     case WM_TIMER:
  1176.         PrinterQueue_Check(hDlg);
  1177.         // Check the queue every 10 seconds
  1178.         SetTimer(hDlg, 1, 10000, NULL);
  1179.         break;
  1180.     case WM_INITMENU:
  1181.         if ((HMENU)wParam != GetMenu(hDlg))
  1182.         {
  1183.             break;
  1184.         }
  1185.         PrinterQueue_InitPrinterMenu(GetSubMenu((HMENU)wParam, 0), pqi);
  1186.         PrinterQueue_InitDocMenu(GetSubMenu((HMENU)wParam, 1), pqi,
  1187.             ListView_GetNextItem(pqi->hwndLV, -1, LVNI_SELECTED));
  1188.         PrinterQueue_InitViewMenu(GetSubMenu((HMENU)wParam, 2), pqi);
  1189.         break;
  1190.     case WM_COMMAND:
  1191.         switch (GET_WM_COMMAND_ID(wParam, lParam))
  1192.         {
  1193.         case IDCANCEL:
  1194.             PostQuitMessage(0);
  1195.             break;
  1196.         case ID_DOCUMENT_PAUSE:
  1197.             if (!Printer_ModifyJob(hDlg, JOB_CONTROL_PAUSE))
  1198.                 goto WarnOnError;
  1199.             break;
  1200.         case ID_DOCUMENT_RESUME:
  1201.             if (!Printer_ModifyJob(hDlg, JOB_CONTROL_RESUME))
  1202.                 goto WarnOnError;
  1203.             break;
  1204.         case ID_DOCUMENT_DELETE:
  1205.             if (!Printer_ModifyJob(hDlg, JOB_CONTROL_CANCEL))
  1206.             {
  1207. WarnOnError:;
  1208. #ifndef WINNT
  1209.                 Printer_WarnOnError(hDlg, pqi->idp.cName, IDS_SECURITYDENIED_JOB);
  1210. #endif
  1211.             }
  1212.             break;
  1213. //      case ID_VIEW_TOOLBAR:
  1214. //          break;
  1215.         case ID_VIEW_STATUSBAR:
  1216.             pqi->fStatusBar = 1-pqi->fStatusBar;
  1217.             ShowWindow(pqi->hwndSB, pqi->fStatusBar ? SW_SHOW : SW_HIDE);
  1218.             PrinterQueue_OnSize(pqi);
  1219.             break;
  1220.         // even though this isn't on the menu any more, printobj.c sends it:
  1221.         // we also have an accelerator for it:
  1222.         case ID_VIEW_REFRESH:
  1223.             PostMessage(hDlg, WM_TIMER, 1, 0L);
  1224.             break;
  1225.         // from cabinetcommand.c: DoAboutChicago
  1226.         case ID_HELP_ABOUT:
  1227.         {
  1228.             TCHAR szChicago[64];
  1229.             LoadString(HINST_THISDLL, IDS_WINDOWS, szChicago, ARRAYSIZE(szChicago));
  1230.             ShellAbout(hDlg, szChicago, NULL, NULL);
  1231.             break;
  1232.         }
  1233.         // from defviewx.c: SFVIDM_HELP_TOPIC
  1234.         case ID_HELP_CONTENTS:
  1235.             //
  1236.             // REVIEW: Should we jump to a topic which describes current
  1237.             //  viewed folder?
  1238.             //
  1239.             WinHelp(hDlg, c_szWindowsHlp, HELP_FINDER, 0);
  1240.             break;
  1241.         default:
  1242.             // Must have been merged in from the printer's context menu
  1243.             Printer_InvokeCommand(hDlg, NULL, &(pqi->idp), wParam-ID_PRINTER_START, 0, NULL);
  1244.             break;
  1245.         }
  1246.         break;
  1247.     case WM_NOTIFY:
  1248.         PrintQueue_Notify(hDlg, (NMHDR *)lParam);
  1249.         break;
  1250.     default:
  1251.         return(FALSE);
  1252.     }
  1253.     return(TRUE);
  1254. }
  1255. void Printer_ViewQueue(HWND hwndStub, LPCTSTR lpszCmdLine, int nCmdShow, LPARAM lParam)
  1256. {
  1257.     LPPRINTER_INFO_5 pPrinter;
  1258.     BOOL fFile = FALSE;
  1259.     pPrinter = Printer_GetPrinterInfoStr(lpszCmdLine, 5);
  1260.     if (pPrinter)
  1261.     {
  1262.         fFile = !lstrcmp(pPrinter->pPortName, c_szFileColon);
  1263.         LocalFree((HLOCAL)pPrinter);
  1264.     }
  1265.     if (fFile)
  1266.     {
  1267.         ShellMessageBox(HINST_THISDLL, hwndStub,
  1268.             MAKEINTRESOURCE(IDS_CANTVIEW_FILEPRN), lpszCmdLine,
  1269.             MB_OK|MB_ICONINFORMATION);
  1270.     }
  1271.     else if (pPrinter)
  1272.     {
  1273.         INITINFO ii;
  1274.         HWND hwndDlg;
  1275.         ii.lpszPrinterName = lpszCmdLine;
  1276.         ii.nCmdShow = nCmdShow;
  1277.         // Call CreateDialog and do our own message loop w/ TranslateAccelerator
  1278.         hwndDlg = CreateDialogParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_PRN_QUEUE), hwndStub,
  1279.             PrinterQueue_DlgProc, (LPARAM)(LPTSTR)&ii);
  1280.         if (hwndDlg)
  1281.         {
  1282.             HACCEL hAccel;
  1283.             MSG msg;
  1284.             hAccel = LoadAccelerators(HINST_THISDLL, MAKEINTRESOURCE(ACCEL_PRN_QUEUE));
  1285.             while (GetMessage(&msg, NULL, 0, 0))
  1286.             {
  1287.                 if (!TranslateAccelerator(hwndDlg, hAccel, &msg))
  1288.                 {
  1289.                     TranslateMessage(&msg);
  1290.                     DispatchMessage(&msg);
  1291.                 }
  1292.             }
  1293.             DestroyWindow(hwndDlg);
  1294.         }
  1295.     }
  1296.     else
  1297.     {
  1298.         // If you rename a printer and then try to open a link to that
  1299.         // printer, we hit this case.  An error message is appropriate?
  1300.         ShellMessageBox(HINST_THISDLL, hwndStub,
  1301.             MAKEINTRESOURCE(IDS_PRINTERNAME_CHANGED), lpszCmdLine,
  1302.             MB_OK|MB_ICONINFORMATION);
  1303.     }
  1304. }
  1305. #endif