Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3115 lines
79 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1995 - 1999
  3. All rights reserved.
  4. Module Name:
  5. queue.cxx
  6. Abstract:
  7. Manages the print queue.
  8. This module is aware of the ListView.
  9. Author:
  10. Albert Ting (AlbertT) 15-Jun-1995
  11. Revision History:
  12. --*/
  13. #include "precomp.hxx"
  14. #pragma hdrstop
  15. #include "notify.hxx"
  16. #include "data.hxx"
  17. #include "printer.hxx"
  18. #include "dragdrop.hxx"
  19. #include "queue.hxx"
  20. #include "time.hxx"
  21. #include "psetup.hxx"
  22. #include "drvsetup.hxx"
  23. #include "instarch.hxx"
  24. #include "portslv.hxx"
  25. #include "dsinterf.hxx"
  26. #include "prtprop.hxx"
  27. #include "propmgr.hxx"
  28. #include "docdef.hxx"
  29. #include "docprop.hxx"
  30. #include "persist.hxx"
  31. #include "rtlmir.hxx"
  32. #include "guids.h"
  33. #if DBG
  34. //#define DBG_QUEUEINFO DBG_INFO
  35. #define DBG_QUEUEINFO DBG_NONE
  36. #endif
  37. const TQueue::POSINFO TQueue::gPQPos = {
  38. TDataNJob::kColumnFieldsSize,
  39. {
  40. JOB_COLUMN_FIELDS,
  41. 0, 0, 0, 0, 0, 0, 0,
  42. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  43. },
  44. {
  45. 200, 80, 80, 60, 100, 140, 120, 80, 80, 80, 80, 80, 80, 80,
  46. 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80
  47. },
  48. {
  49. sizeof( WINDOWPLACEMENT ), 0, SW_SHOW,
  50. { 0, 0 }, { 0, 0 }, { 50, 100, 612, 300 }
  51. },
  52. TRUE,
  53. TRUE,
  54. {
  55. 0, 1, 2, 3, 4, 5, 6, 7,
  56. 8, 9, 10, 11, 12, 13, 14, 15,
  57. 16, 17, 18, 19, 20, 21, 22, 23,
  58. 24, 25, 26, 27, 28, 29, 30, 31,
  59. 0,
  60. }
  61. };
  62. const DWORD
  63. gadwFieldTable[] = {
  64. #define DEFINE( field, x, table, y, offset ) table,
  65. #include "ntfyjob.h"
  66. #undef DEFINE
  67. 0
  68. };
  69. /********************************************************************
  70. Status translation tables:
  71. ********************************************************************/
  72. const STATUS_MAP gaStatusMapPrinter[] = {
  73. { PRINTER_STATUS_PENDING_DELETION, IDS_STATUS_DELETING },
  74. { PRINTER_STATUS_USER_INTERVENTION,IDS_STATUS_USER_INTERVENTION },
  75. { PRINTER_STATUS_PAPER_JAM, IDS_STATUS_PAPER_JAM },
  76. { PRINTER_STATUS_PAPER_OUT, IDS_STATUS_PAPER_OUT },
  77. { PRINTER_STATUS_MANUAL_FEED, IDS_STATUS_MANUAL_FEED },
  78. { PRINTER_STATUS_DOOR_OPEN, IDS_STATUS_DOOR_OPEN },
  79. { PRINTER_STATUS_NOT_AVAILABLE, IDS_STATUS_NOT_AVAILABLE },
  80. { PRINTER_STATUS_PAPER_PROBLEM, IDS_STATUS_PAPER_PROBLEM },
  81. { PRINTER_STATUS_OFFLINE, IDS_STATUS_OFFLINE },
  82. { PRINTER_STATUS_PAUSED, IDS_STATUS_PAUSED },
  83. { PRINTER_STATUS_OUT_OF_MEMORY, IDS_STATUS_OUT_OF_MEMORY },
  84. { PRINTER_STATUS_NO_TONER, IDS_STATUS_NO_TONER },
  85. { PRINTER_STATUS_TONER_LOW, IDS_STATUS_TONER_LOW },
  86. { PRINTER_STATUS_PAGE_PUNT, IDS_STATUS_PAGE_PUNT },
  87. { PRINTER_STATUS_OUTPUT_BIN_FULL, IDS_STATUS_OUTPUT_BIN_FULL },
  88. { PRINTER_STATUS_SERVER_UNKNOWN, IDS_STATUS_SERVER_UNKNOWN },
  89. { PRINTER_STATUS_IO_ACTIVE, IDS_STATUS_IO_ACTIVE },
  90. { PRINTER_STATUS_BUSY, IDS_STATUS_BUSY },
  91. { PRINTER_STATUS_WAITING, IDS_STATUS_WAITING },
  92. { PRINTER_STATUS_PROCESSING, IDS_STATUS_PROCESSING },
  93. { PRINTER_STATUS_INITIALIZING, IDS_STATUS_INITIALIZING },
  94. { PRINTER_STATUS_WARMING_UP, IDS_STATUS_WARMING_UP },
  95. { PRINTER_STATUS_PRINTING, IDS_STATUS_PRINTING },
  96. { PRINTER_STATUS_POWER_SAVE, IDS_STATUS_POWER_SAVE },
  97. { 0, 0 }
  98. };
  99. const STATUS_MAP gaStatusMapJob[] = {
  100. { JOB_STATUS_DELETING, IDS_STATUS_DELETING },
  101. { JOB_STATUS_PAPEROUT, IDS_STATUS_PAPER_OUT },
  102. { JOB_STATUS_ERROR , IDS_STATUS_ERROR },
  103. { JOB_STATUS_OFFLINE , IDS_STATUS_OFFLINE },
  104. { JOB_STATUS_PAUSED , IDS_STATUS_PAUSED },
  105. { JOB_STATUS_SPOOLING, IDS_STATUS_SPOOLING },
  106. { JOB_STATUS_PRINTING, IDS_STATUS_PRINTING },
  107. { JOB_STATUS_PRINTED , IDS_STATUS_PRINTED },
  108. { JOB_STATUS_RESTART , IDS_STATUS_RESTART },
  109. { JOB_STATUS_COMPLETE, IDS_STATUS_COMPLETE },
  110. { 0, 0 }
  111. };
  112. /********************************************************************
  113. MenuHelp
  114. ********************************************************************/
  115. UINT
  116. TQueue::gauMenuHelp[kMenuHelpMax] = {
  117. MH_PRINTER, MH_PRINTER,
  118. 0, 0
  119. };
  120. TQueue::
  121. TQueue(
  122. IN TPrintLib *pPrintLib,
  123. IN LPCTSTR pszPrinter,
  124. IN HANDLE hEventClose
  125. ) : _hwndTB( NULL ), _hwndLV( NULL ), _hwndSB( NULL ),
  126. _idsConnectStatus( 0 ), _dwErrorStatus( 0 ), _dwAttributes( 0 ),
  127. _dwStatusPrinter( 0 ), _hEventClose( hEventClose ),
  128. _bWindowClosing( FALSE ), _cItems( -1 ),
  129. _pDropTarget( NULL )
  130. /*++
  131. Routine Description:
  132. Create the Queue object. The gpPrintLib has already been incremented
  133. for us, so we do not need to do it here.
  134. Must be in UI thread so that all UI is handled by one thread.
  135. Arguments:
  136. hwndOwner - Owning window.
  137. pszPrinter - Printer to open.
  138. nCmdShow - Show command for window.
  139. hEventClose - Event to be triggered when window closes (this
  140. event is _not_ adopted and must be closed by callee). Used
  141. when the callee wants to know when the user dismisses the
  142. Queue UI.
  143. Return Value:
  144. --*/
  145. {
  146. ASSERT(pPrintLib);
  147. SINGLETHREAD(UIThread);
  148. //
  149. // This must always occur, so do not fail before it. We do
  150. // are using a RefLock because we need to store the _pPrintLib
  151. // pointer.
  152. //
  153. _pPrintLib.vAcquire(pPrintLib);
  154. SaveSelection._pSelection = NULL;
  155. _pPrinter = TPrinter::pNew( (TQueue*)this, pszPrinter, 0 );
  156. if( _pPrinter && !VALID_PTR(_pPrinter) )
  157. {
  158. //
  159. // bValid is looking for _pPrinter to determine if the object is valid.
  160. //
  161. _pPrinter->vDelete();
  162. _pPrinter = NULL;
  163. }
  164. _bDefaultPrinter = CheckDefaultPrinter( pszPrinter ) == kDefault;
  165. }
  166. TQueue::
  167. ~TQueue(
  168. VOID
  169. )
  170. {
  171. //
  172. // Delete from our linked list if it's linked.
  173. //
  174. if( Queue_bLinked( )){
  175. CCSLock::Locker CSL( *gpCritSec );
  176. Queue_vDelinkSelf();
  177. }
  178. }
  179. //
  180. // this function is taken from the comctl32 code since shfuson doesn't implement a warpper
  181. // and we need to take care of calling CreateWindowEx appropriately.
  182. //
  183. HWND WINAPI FusionWrapper_CreateStatusWindow(LONG style, LPCTSTR pszText, HWND hwndParent, UINT uID)
  184. {
  185. // remove border styles to fix capone and other apps
  186. return CreateWindowEx(0, STATUSCLASSNAME, pszText, style & ~(WS_BORDER | CCS_NODIVIDER),
  187. -100, -100, 10, 10, hwndParent, INT2PTR(uID, HMENU), ghInst, NULL);
  188. }
  189. BOOL
  190. TQueue::
  191. bInitialize(
  192. IN HWND hwndOwner,
  193. IN INT nCmdShow
  194. )
  195. /*++
  196. Routine Description:
  197. Creates the queue window and thunks it to our object.
  198. Arguments:
  199. Return Value:
  200. --*/
  201. {
  202. SINGLETHREAD(UIThread);
  203. HIMAGELIST himl;
  204. DWORD dwExStyle;
  205. if( !bValid() ) {
  206. goto Error;
  207. }
  208. _hwnd = CreateWindowEx( bIsBiDiLocalizedSystem() ? kExStyleRTLMirrorWnd : 0,
  209. gszClassName,
  210. pPrinter()->strPrinter(),
  211. WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  212. CW_USEDEFAULT, CW_USEDEFAULT,
  213. CW_USEDEFAULT, CW_USEDEFAULT,
  214. hwndOwner,
  215. NULL,
  216. ghInst,
  217. (LPVOID)this );
  218. if( !_hwnd ){
  219. goto Error;
  220. }
  221. _hwndSB = FusionWrapper_CreateStatusWindow( WS_CHILD | SBARS_SIZEGRIP |
  222. WS_CLIPSIBLINGS,
  223. NULL,
  224. _hwnd,
  225. IDD_STATUSBAR );
  226. if( !_hwndSB ){
  227. goto Error;
  228. }
  229. _hwndLV = CreateWindowEx( WS_EX_CLIENTEDGE,
  230. WC_LISTVIEW,
  231. gszNULL,
  232. WS_CHILD | WS_VISIBLE | WS_TABSTOP |
  233. WS_CLIPSIBLINGS | LVS_REPORT | LVS_NOSORTHEADER,
  234. 0, 0, 0, 0,
  235. _hwnd,
  236. (HMENU)IDD_LISTVIEW,
  237. ghInst,
  238. NULL );
  239. if( !_hwndLV ){
  240. goto Error;
  241. }
  242. if( SUCCEEDED(DragDrop::CreatePrintQueueDT(IID_IPrintQueueDT, (void **)&_pDropTarget)) )
  243. {
  244. _pDropTarget->RegisterDragDrop(_hwndLV, _pPrinter);
  245. }
  246. //
  247. // Enable header re-ordering.
  248. //
  249. dwExStyle = ListView_GetExtendedListViewStyle( _hwndLV );
  250. ListView_SetExtendedListViewStyle( _hwndLV, dwExStyle | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP );
  251. //
  252. // !! LATER !! - add toolbar.
  253. //
  254. // NO toolbar for now.
  255. //
  256. himl = ImageList_Create( gcxSmIcon,
  257. gcySmIcon,
  258. ILC_MASK,
  259. 1,
  260. 3 );
  261. if( himl ){
  262. ImageList_SetBkColor( himl, GetSysColor( COLOR_WINDOW ));
  263. HICON hIcon = (HICON)LoadImage( ghInst,
  264. MAKEINTRESOURCE( IDI_DOCUMENT ),
  265. IMAGE_ICON,
  266. gcxSmIcon, gcySmIcon,
  267. LR_DEFAULTCOLOR );
  268. if( hIcon ){
  269. INT iIndex = ImageList_AddIcon( himl, hIcon );
  270. //
  271. // The return value to ImageList_AddIcon should be zero
  272. // since this is the first image we are adding.
  273. //
  274. if( iIndex == 0 ){
  275. SendMessage( _hwndLV, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)himl );
  276. } else {
  277. DBGMSG( DBG_WARN,
  278. ( "Queue.ctr: ImageList_AddIcon failed %d %d\n",
  279. iIndex, GetLastError( )));
  280. }
  281. DestroyIcon( hIcon );
  282. } else {
  283. DBGMSG( DBG_ERROR,
  284. ( "Queue.ctr: Failed to load hIcon %d\n",
  285. GetLastError( )));
  286. }
  287. }
  288. SetWindowLongPtr( _hwnd, DWLP_USER, (LONG_PTR)this );
  289. {
  290. //
  291. // Retrieve the saved windows settings for the printer.
  292. //
  293. POSINFO sPos = gPQPos;
  294. //
  295. // Get the the queue position persistant setting if it does not already
  296. // exist create it. Then read the posistion from the registry.
  297. //
  298. {
  299. TPersist Persist( gszPrinterPositions, TPersist::kCreate|TPersist::kRead );
  300. if( VALID_OBJ( Persist ) )
  301. {
  302. DWORD dwSize = sizeof( sPos );
  303. TStatusB bStatus;
  304. bStatus DBGCHK = Persist.bRead( pPrinter()->strPrinter(), &sPos, dwSize );
  305. }
  306. }
  307. _uColMax = sPos.uColMax;
  308. _bStatusBar = sPos.bStatusBar;
  309. ShowWindow( _hwndSB, _bStatusBar ? SW_SHOW : SW_HIDE );
  310. vAddColumns( &sPos );
  311. LoadPrinterIcons( _pPrinter->strPrinter(),
  312. &_shIconLarge,
  313. &_shIconSmall );
  314. SendMessage( _hwnd, WM_SETICON, ICON_BIG, (LPARAM)(HICON)_shIconLarge );
  315. SendMessage( _hwnd, WM_SETICON, ICON_SMALL, (LPARAM)(HICON)_shIconSmall );
  316. sPos.wp.showCmd = nCmdShow;
  317. SetWindowPlacement( _hwnd, &sPos.wp );
  318. //
  319. // Restore the column order
  320. //
  321. ListView_SetColumnOrderArray( _hwndLV, _uColMax, sPos.anColOrder );
  322. }
  323. //
  324. // Open the printer.
  325. //
  326. _pPrintLib->bJobAdd( _pPrinter,
  327. TPrinter::kExecReopen );
  328. //
  329. // hwndLV is our valid check.
  330. //
  331. //
  332. // Insert into our linked list, but only if valid.
  333. //
  334. {
  335. CCSLock::Locker CSL( *gpCritSec );
  336. SPLASSERT( bValid( ));
  337. _pPrintLib->Queue_vAdd( this );
  338. }
  339. return TRUE;
  340. Error:
  341. return FALSE;
  342. }
  343. VOID
  344. TQueue::
  345. vWindowClosing(
  346. VOID
  347. )
  348. /*++
  349. Routine Description:
  350. Called when window is closing.
  351. Arguments:
  352. Return Value:
  353. --*/
  354. {
  355. SINGLETHREAD(UIThread);
  356. //
  357. // Mark ourselves as closing the windows. This prevents us from
  358. // trying to send more hBlocks to the message queue.
  359. //
  360. _bWindowClosing = TRUE;
  361. SendMessage( _hwnd, WM_SETICON, ICON_SMALL, 0 );
  362. SendMessage( _hwnd, WM_SETICON, ICON_BIG, 0 );
  363. //
  364. // Force cleanup of GenWin.
  365. //
  366. vForceCleanup();
  367. //
  368. // Disassociate the printer from the queue. At this stage, the
  369. // window is marked as closed, so we won't put any more hBlocks into
  370. // the message queue. If we are being accessed by another thread,
  371. // we won't delete ourselves until it has released it's reference
  372. // to us.
  373. //
  374. if( _pPrinter )
  375. {
  376. _pPrinter->vDelete();
  377. }
  378. if( _hEventClose )
  379. {
  380. // notify the caller we are about to go away
  381. SetEvent(_hEventClose);
  382. }
  383. }
  384. VOID
  385. TQueue::
  386. vSaveColumns(
  387. VOID
  388. )
  389. {
  390. //
  391. // Save the position info if we had a valid window.
  392. //
  393. if( bValid( )){
  394. POSINFO sPos = { 0 };
  395. sPos.uColMax = _uColMax;
  396. sPos.wp.length = sizeof( WINDOWPLACEMENT );
  397. GetWindowPlacement( _hwnd, &sPos.wp );
  398. //
  399. // Get the column widths.
  400. //
  401. UINT i;
  402. PFIELD pFields = pGetColFields();
  403. for( i=0; i < _uColMax; ++i ){
  404. sPos.anWidth[i] = ListView_GetColumnWidth( _hwndLV, i );
  405. sPos.aField[i] = pFields[i];
  406. }
  407. sPos.bStatusBar = _bStatusBar;
  408. //
  409. // Get the list views column order.
  410. //
  411. ListView_GetColumnOrderArray( _hwndLV, _uColMax, sPos.anColOrder );
  412. //
  413. // Persist the queue position.
  414. //
  415. TPersist Persist( gszPrinterPositions, TPersist::kOpen|TPersist::kWrite );
  416. if( VALID_OBJ( Persist ) )
  417. {
  418. TCHAR szPrinter[kPrinterBufMax];
  419. TStatusB bStatus;
  420. bStatus DBGCHK = Persist.bWrite( _pPrinter->pszPrinterName( ARRAYSIZE(szPrinter), szPrinter ), &sPos, sizeof( sPos ) );
  421. }
  422. }
  423. }
  424. VOID
  425. TQueue::
  426. vAddColumns(
  427. IN const POSINFO* pPosInfo
  428. )
  429. {
  430. SINGLETHREAD(UIThread);
  431. LV_COLUMN col;
  432. TCHAR szColName[kColStrMax];
  433. UINT i;
  434. for( i=0; i < pPosInfo->uColMax; ++i ){
  435. //
  436. // !! SERVERQUEUE !!
  437. //
  438. // Add IDS_HEAD_DELTA if server queue.
  439. //
  440. LoadString( ghInst,
  441. IDS_HEAD + pPosInfo->aField[i],
  442. szColName,
  443. COUNTOF( szColName ));
  444. col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  445. col.fmt = LVCFMT_LEFT;
  446. col.pszText = (LPTSTR)szColName;
  447. col.cchTextMax = 0;
  448. col.cx = pPosInfo->anWidth[i];
  449. col.iSubItem = pPosInfo->aField[i];
  450. ListView_InsertColumn(_hwndLV, i, &col);
  451. }
  452. }
  453. /********************************************************************
  454. Message handler.
  455. ********************************************************************/
  456. LRESULT
  457. TQueue::
  458. nHandleMessage(
  459. IN UINT uMsg,
  460. IN WPARAM wParam,
  461. IN LPARAM lParam
  462. )
  463. {
  464. switch(uMsg) {
  465. case WM_PRINTLIB_STATUS: {
  466. INFO Info;
  467. Info.dwData = (DWORD)lParam;
  468. //
  469. // Status change request from worker thread.
  470. //
  471. vContainerChangedHandler( (CONTAINER_CHANGE)wParam, Info );
  472. break;
  473. }
  474. case WM_NOTIFY:
  475. if( wParam == IDD_LISTVIEW ){
  476. return lrOnLVNotify( lParam );
  477. }
  478. break;
  479. case WM_SETFOCUS:
  480. SetFocus( _hwndLV );
  481. break;
  482. case WM_CREATE:
  483. //
  484. // The window was successfully created, so increment the
  485. // reference count. The corresponding decrement is when the
  486. // windows is destroyed: WM_NCDESTROY.
  487. //
  488. vIncRef();
  489. break;
  490. case WM_DESTROY:
  491. if( _pDropTarget )
  492. {
  493. // revoke drag & drop
  494. _pDropTarget->RevokeDragDrop();
  495. // this a naked COM pointer - release it.
  496. _pDropTarget->Release();
  497. _pDropTarget = NULL;
  498. }
  499. vSaveColumns();
  500. break;
  501. case WM_NCDESTROY:
  502. {
  503. //
  504. // Deleting ourselves must be the absolute last thing that
  505. // we do; we don't want any more messages to get processed
  506. // after that.
  507. //
  508. // This is necessary because in the synchronous case, we
  509. // (thread A) notifies the waiter (thread W) that the queue
  510. // has gone away. Then we (thread A) call DefWindowProc
  511. // with WM_NCDESTROY, which lets comctl32 acquire it's global
  512. // critical section to destory the image list.
  513. //
  514. // In rundll32, thread W terminates since the queue is gone,
  515. // killing thread A which hold the comctl32 global cs. Then
  516. // thread w tries to call comctl32's DllEntryPoint with
  517. // PROCESS_DETACH, which attempts to acquire the global cs.
  518. // This hangs and the process never terminates.
  519. //
  520. LRESULT lResult = DefWindowProc( hwnd(), uMsg, wParam, lParam );
  521. //
  522. // Save out the settings since we are closing the window.
  523. //
  524. vWindowClosing();
  525. //
  526. // Decrement the reference count since we are deleting.
  527. //
  528. vDecRefDelete();
  529. return lResult;
  530. }
  531. case WM_APPCOMMAND:
  532. {
  533. if( APPCOMMAND_BROWSER_REFRESH == GET_APPCOMMAND_LPARAM(lParam) )
  534. {
  535. //
  536. // Execute refresh.
  537. //
  538. SendMessage( _hwnd, WM_COMMAND, MAKELONG(IDM_REFRESH,0), 0);
  539. }
  540. break;
  541. }
  542. case WM_COMMAND:
  543. {
  544. lParam = 0;
  545. TCHAR szPrinterBuffer[kPrinterBufMax];
  546. LPTSTR pszPrinter;
  547. switch( GET_WM_COMMAND_ID( wParam, lParam )){
  548. case IDM_PRINTER_SET_DEFAULT:
  549. //
  550. // Always write out the default string. User can't
  551. // unset default printer.
  552. //
  553. pszPrinter = _pPrinter->pszPrinterName( ARRAYSIZE(szPrinterBuffer), szPrinterBuffer );
  554. //
  555. // !! LATER !!
  556. //
  557. // Put up error message if fails.
  558. //
  559. SetDefaultPrinter( szPrinterBuffer );
  560. break;
  561. case IDM_PRINTER_SHARING:
  562. //
  563. // Put up printer properties. If sharing was selected,
  564. // then go directly to that page.
  565. //
  566. lParam = TPrinterData::kPropSharing;
  567. //
  568. // Fall through to printer properties.
  569. //
  570. case IDM_PRINTER_PROPERTIES:
  571. pszPrinter = _pPrinter->pszPrinterName( ARRAYSIZE(szPrinterBuffer), szPrinterBuffer );
  572. vPrinterPropPages(
  573. NULL,
  574. pszPrinter,
  575. SW_SHOWNORMAL,
  576. lParam );
  577. break;
  578. case IDM_PRINTER_DOCUMENT_DEFAULTS:
  579. pszPrinter = _pPrinter->pszPrinterName( ARRAYSIZE(szPrinterBuffer), szPrinterBuffer );
  580. vDocumentDefaults(
  581. NULL,
  582. pszPrinter,
  583. SW_SHOWNORMAL,
  584. lParam );
  585. break;
  586. case IDM_PRINTER_CLOSE:
  587. DestroyWindow( _hwnd );
  588. return 0;
  589. case IDM_STATUS_BAR:
  590. {
  591. RECT rc;
  592. _bStatusBar = !_bStatusBar;
  593. ShowWindow( _hwndSB,
  594. _bStatusBar ?
  595. SW_SHOW :
  596. SW_HIDE );
  597. GetClientRect( _hwnd, &rc );
  598. SendMessage( _hwnd,
  599. WM_SIZE,
  600. SIZE_RESTORED,
  601. MAKELONG( rc.right, rc.bottom ));
  602. break;
  603. }
  604. case IDM_PRINTER_INSTALL:
  605. if( !CheckRestrictions(_hwnd, REST_NOPRINTERADD) )
  606. {
  607. TStatusB bStatus;
  608. TCHAR szPrinter[kPrinterBufMax];
  609. LPTSTR pszPrinterName;
  610. pszPrinterName = _pPrinter->pszPrinterName(ARRAYSIZE(szPrinter), szPrinter);
  611. bStatus DBGCHK = bPrinterSetup( _hwnd,
  612. MSP_NETPRINTER,
  613. ARRAYSIZE(szPrinter),
  614. pszPrinterName,
  615. NULL,
  616. NULL );
  617. }
  618. break;
  619. case IDM_REFRESH:
  620. _pPrintLib->bJobAdd( _pPrinter,
  621. TPrinter::kExecRefreshAll );
  622. _pPrinter->vCommandRequested();
  623. break;
  624. case IDM_HELP_CONTENTS:
  625. PrintUIHtmlHelp( _hwnd, gszHtmlPrintingHlp, HH_DISPLAY_TOPIC, reinterpret_cast<ULONG_PTR>( gszHelpQueueId ) );
  626. break;
  627. case IDM_HELP_TROUBLESHOOTER:
  628. {
  629. ShellExecute( _hwnd, TEXT("open"), TEXT("helpctr.exe"), gszHelpTroubleShooterURL, NULL, SW_SHOWNORMAL );
  630. }
  631. break;
  632. case IDM_HELP_ABOUT:
  633. {
  634. TString strWindows;
  635. TString strTitle;
  636. LCID lcid;
  637. BOOL bNeedLRM;
  638. //
  639. // if we have Arabic/Hebrew locale we add the text as LRM + Text + LRM
  640. // to force left-to-right reading (LRM is the unicode character 0x200E)
  641. //
  642. lcid = GetUserDefaultLCID();
  643. bNeedLRM = (PRIMARYLANGID(LANGIDFROMLCID(lcid))== LANG_ARABIC) ||
  644. (PRIMARYLANGID(LANGIDFROMLCID(lcid))== LANG_HEBREW);
  645. if (bNeedLRM)
  646. {
  647. strTitle.bUpdate(TEXT("\x200E"));
  648. }
  649. strWindows.bLoadString( ghInst, IDS_WINDOWS );
  650. strTitle.bCat(strWindows);
  651. if (bNeedLRM)
  652. {
  653. strTitle.bCat(TEXT("\x200E"));
  654. }
  655. ShellAbout( _hwnd, strTitle, NULL, NULL );
  656. }
  657. break;
  658. default:
  659. return lrProcessCommand( GET_WM_COMMAND_ID( wParam, lParam ));
  660. }
  661. break;
  662. }
  663. case WM_ACTIVATE:
  664. //
  665. // We must pass the active window to TranslateAccelerator,
  666. // so when the active window for our app changes, make
  667. // a note of it.
  668. //
  669. if( LOWORD( wParam ) & ( WA_ACTIVE | WA_CLICKACTIVE )){
  670. ghwndActive = _hwnd;
  671. }
  672. break;
  673. case WM_INITMENU:
  674. {
  675. HMENU hMenu = GetMenu( _hwnd );
  676. if( hMenu ){
  677. HMENU hTemp = GetSubMenu( hMenu, 0 );
  678. if( hTemp ){
  679. vInitPrinterMenu( hTemp );
  680. }
  681. hTemp = GetSubMenu( hMenu, 1 );
  682. if( hTemp ){
  683. vInitDocMenu( FALSE, hTemp );
  684. }
  685. hTemp = GetSubMenu( hMenu, 2 );
  686. if( hTemp ){
  687. vInitViewMenu( hTemp );
  688. }
  689. }
  690. }
  691. break;
  692. case WM_MENUSELECT:
  693. if( _bStatusBar ){
  694. MenuHelp( WM_MENUSELECT,
  695. wParam,
  696. lParam,
  697. GetMenu( _hwnd ),
  698. ghInst,
  699. _hwndSB,
  700. gauMenuHelp );
  701. }
  702. break;
  703. case WM_SIZE:
  704. if( wParam != SIZE_MINIMIZED ){
  705. UINT dy = 0;
  706. RECT rc;
  707. SendMessage( _hwndSB,
  708. WM_SIZE,
  709. wParam,
  710. lParam );
  711. GetWindowRect( _hwndSB,
  712. &rc );
  713. //
  714. // If the status bar exists, then we must move it to the
  715. // bottom and squeeze the listview slightly higher.
  716. //
  717. if( _bStatusBar ){
  718. dy = rc.bottom - rc.top;
  719. }
  720. INT aiPanes[3];
  721. aiPanes[0] = 0;
  722. aiPanes[1] = (rc.right - rc.left)/2;
  723. aiPanes[2] = -1;
  724. //
  725. // Put three panes there.
  726. //
  727. SendMessage( _hwndSB,
  728. SB_SETPARTS,
  729. 3,
  730. (LPARAM)aiPanes );
  731. //
  732. // Move this list view to match the parent window.
  733. //
  734. MoveWindow( _hwndLV,
  735. 0, 0,
  736. LOWORD( lParam ), HIWORD( lParam ) - dy,
  737. TRUE );
  738. }
  739. break;
  740. case WM_COPYDATA:
  741. return bOnCopyData( wParam, lParam );
  742. case WM_SETTINGCHANGE:
  743. {
  744. //
  745. // Check if the default printer has changed, to update the icon.
  746. //
  747. vCheckDefaultPrinterChanged();
  748. //
  749. // This message is sent when the system date/time format
  750. // has been changed so we need to repaint the list view.
  751. //
  752. InvalidateRect(_hwndLV, NULL, TRUE);
  753. }
  754. break;
  755. default:
  756. return DefWindowProc( hwnd(), uMsg, wParam, lParam );
  757. }
  758. return 0;
  759. }
  760. LRESULT
  761. TQueue::
  762. lrOnLVNotify(
  763. IN LPARAM lParam
  764. )
  765. {
  766. switch( ((LPNMHDR)lParam)->code ){
  767. case LVN_GETDISPINFO:
  768. return lrOnLVGetDispInfo( (LV_DISPINFO*)lParam );
  769. case LVN_BEGINDRAG:
  770. case LVN_BEGINRDRAG:
  771. // return lrOnLVBeginDrag( reinterpret_cast<const NM_LISTVIEW*>(lParam) );
  772. return 0; // turn off this feature for XP, will turn it back on for Blackcomb
  773. case NM_DBLCLK:
  774. return lrOnLVDoubleClick();
  775. case NM_RCLICK:
  776. return lrOnLVRClick( (NMHDR*)lParam );
  777. }
  778. return 0;
  779. }
  780. LRESULT
  781. TQueue::
  782. lrOnLVGetDispInfo(
  783. IN const LV_DISPINFO* plvdi
  784. )
  785. /*++
  786. Routine Description:
  787. Process the display info message for list views.
  788. Arguments:
  789. Return Value:
  790. --*/
  791. {
  792. //
  793. // If this message is not going to retrieve the text, return immediately
  794. //
  795. if( !(plvdi->item.mask & LVIF_TEXT) )
  796. {
  797. return 0;
  798. }
  799. LPTSTR pszText = plvdi->item.pszText;
  800. pszText[0] = 0;
  801. size_t cchTextMax = plvdi->item.cchTextMax;
  802. FIELD Field = gPQPos.aField[plvdi->item.iSubItem];
  803. INFO Info = _pPrinter->pData()->GetInfo( (HITEM)plvdi->item.lParam,
  804. plvdi->item.iSubItem );
  805. DATA_INDEX DataIndex = 0;
  806. DWORD dwPrinted;
  807. TCHAR szTemp[MAX_PATH] = {0};
  808. //
  809. // Special case certain fields:
  810. //
  811. // JOB_NOTIFY_FIELD_STATUS_STRING - add STATUS
  812. // JOB_NOTIFY_FIELD_TOTAL_BYTES - add BYTES_PRINTED
  813. // JOB_NOTIFY_FIELD_TOTAL_PAGES - add PAGES_PRINTED
  814. //
  815. switch( Field ){
  816. case JOB_NOTIFY_FIELD_STATUS_STRING:
  817. {
  818. DWORD dwStatus = 0;
  819. COUNT cch = cchTextMax;
  820. //
  821. // If the print device wants multiple job status strings or
  822. // current job status string is null then create a job status
  823. // string using the job status bits.
  824. //
  825. if( _pPrinter->eJobStatusStringType() == TPrinter::kMultipleJobStatusString ||
  826. !Info.pszData ||
  827. !Info.pszData[0] ){
  828. dwStatus = _pPrinter->pData()->GetInfo(
  829. (HITEM)plvdi->item.lParam,
  830. TDataNJob::kIndexStatus ).dwData;
  831. pszText = pszStatusString( pszText,
  832. cch, // Note: passed by reference
  833. dwStatus,
  834. FALSE,
  835. FALSE,
  836. gaStatusMapJob );
  837. }
  838. //
  839. // Add the status string, but not if it's PRINTING
  840. // and we already have PRINTING set.
  841. //
  842. if( Info.pszData && Info.pszData[0] ){
  843. TString strPrinting;
  844. TStatusB bStatus = TRUE;
  845. if( dwStatus & JOB_STATUS_PRINTING ){
  846. bStatus DBGCHK = strPrinting.bLoadString(ghInst, IDS_STATUS_PRINTING);
  847. if( bStatus ){
  848. bStatus DBGCHK = lstrcmpi( Info.pszData, strPrinting ) ? TRUE : FALSE;
  849. }
  850. }
  851. if( bStatus ){
  852. //
  853. // Add separator if necessary.
  854. //
  855. if( pszText != plvdi->item.pszText ){
  856. pszText = pszStrCat( pszText, gszStatusSeparator, cch );
  857. }
  858. lstrcpyn( pszText, Info.pszData, cch );
  859. }
  860. }
  861. DBGMSG( DBG_TRACE, ("Job info String: " TSTR "\n", pszText ) );
  862. return 0;
  863. }
  864. case JOB_NOTIFY_FIELD_TOTAL_BYTES:
  865. {
  866. dwPrinted = _pPrinter->pData()->GetInfo(
  867. (HITEM)plvdi->item.lParam,
  868. TDataNJob::kIndexBytesPrinted ).dwData;
  869. if( dwPrinted )
  870. {
  871. TString strForwardSlash;
  872. TStatusB bStatus;
  873. bStatus DBGCHK = strForwardSlash.bLoadString(ghInst, IDS_QUEUE_FORWARD_SLASH);
  874. if( bStatus )
  875. {
  876. TCHAR szPrinted[64];
  877. TCHAR szTotal[64];
  878. StrFormatByteSize(dwPrinted, szPrinted, ARRAYSIZE(szPrinted));
  879. StrFormatByteSize(Info.dwData, szTotal, ARRAYSIZE(szTotal));
  880. wnsprintf(plvdi->item.pszText, plvdi->item.cchTextMax, TEXT("%s%s%s"),
  881. szPrinted, static_cast<LPCTSTR>(strForwardSlash), szTotal);
  882. }
  883. }
  884. else
  885. {
  886. if( Info.dwData )
  887. {
  888. TCHAR szTotal[64];
  889. StrFormatByteSize(Info.dwData, szTotal, ARRAYSIZE(szTotal));
  890. wnsprintf(plvdi->item.pszText, plvdi->item.cchTextMax, TEXT("%s"), szTotal);
  891. }
  892. }
  893. return 0;
  894. }
  895. case JOB_NOTIFY_FIELD_TOTAL_PAGES:
  896. dwPrinted = _pPrinter->pData()->GetInfo(
  897. (HITEM)plvdi->item.lParam,
  898. TDataNJob::kIndexPagesPrinted ).dwData;
  899. //
  900. // when a downlevel document is printed (the doc goes directly to the port) StartDoc/EndDoc
  901. // are not called and the spooler doesn't know the total pages of the document. in this case
  902. // we don't display the pages info since it is not acurate.
  903. //
  904. if( Info.dwData ){
  905. if( dwPrinted ){
  906. AddCommas( dwPrinted, szTemp );
  907. StringCchCopy( pszText, cchTextMax, szTemp );
  908. StringCchCat( pszText, cchTextMax, TEXT( "/" ) );
  909. }
  910. szTemp[0] = 0;
  911. AddCommas( Info.dwData, szTemp );
  912. StringCchCat( pszText, cchTextMax, szTemp );
  913. }
  914. else
  915. {
  916. TStatusB bStatus;
  917. TString strText;
  918. bStatus DBGCHK = strText.bLoadString(ghInst, IDS_TEXT_NA);
  919. if( bStatus )
  920. {
  921. //
  922. // just display "N/A" text (we don't know the total pages)
  923. //
  924. lstrcpyn(plvdi->item.pszText, strText, plvdi->item.cchTextMax);
  925. }
  926. }
  927. return 0;
  928. default:
  929. break;
  930. }
  931. switch( gadwFieldTable[Field] ){
  932. case TABLE_STRING:
  933. //
  934. // If we have data, reassign the pointer,
  935. // else leave it pointing to szText, which is "".
  936. //
  937. if( Info.pszData ){
  938. lstrcpyn( pszText, Info.pszData, kStrMax );
  939. }
  940. break;
  941. case TABLE_DWORD:
  942. if( Info.dwData ){
  943. AddCommas( Info.dwData, pszText );
  944. }
  945. break;
  946. case TABLE_TIME:
  947. if( Info.pSystemTime ){
  948. SYSTEMTIME LocalTime;
  949. if ( !SystemTimeToTzSpecificLocalTime(
  950. NULL,
  951. Info.pSystemTime,
  952. &LocalTime )) {
  953. DBGMSG( DBG_MIN, ( "[SysTimeToTzSpecLocalTime failed %d]\n",
  954. ::GetLastError( )));
  955. break;
  956. }
  957. if( !GetTimeFormat( LOCALE_USER_DEFAULT,
  958. 0,
  959. &LocalTime,
  960. NULL,
  961. szTemp,
  962. ARRAYSIZE(szTemp) )){
  963. DBGMSG( DBG_MIN, ( "[No Time %d], ", ::GetLastError( )));
  964. break;
  965. }
  966. StringCchCat( pszText, cchTextMax, szTemp );
  967. StringCchCat( pszText, cchTextMax, TEXT(" ") );
  968. if( !GetDateFormat( LOCALE_USER_DEFAULT,
  969. 0,
  970. &LocalTime,
  971. NULL,
  972. szTemp,
  973. ARRAYSIZE(szTemp) )){
  974. DBGMSG( DBG_MIN, ( "[No Date %d]\n", ::GetLastError( )));
  975. break;
  976. }
  977. StringCchCat( pszText, cchTextMax, szTemp );
  978. }
  979. break;
  980. default:
  981. DBGMSG( DBG_MIN, ( "[?tab %d %x]\n",
  982. Field,
  983. Info.pvData ));
  984. break;
  985. }
  986. return 0;
  987. }
  988. LRESULT
  989. TQueue::
  990. lrOnLVBeginDrag(
  991. const NM_LISTVIEW *plv
  992. )
  993. /*++
  994. Routine Description:
  995. Initiates drag & drop operation
  996. Arguments:
  997. Standard for LVN_BEGINDRAG & LVN_BEGINRDRAG
  998. Return Value:
  999. Standard for LVN_BEGINDRAG & LVN_BEGINRDRAG
  1000. --*/
  1001. {
  1002. LVITEM lvi = {0};
  1003. lvi.iItem = plv->iItem;
  1004. lvi.iSubItem = plv->iSubItem;
  1005. lvi.mask = LVIF_PARAM;
  1006. if( ListView_GetItem(_hwndLV, &lvi) )
  1007. {
  1008. DragDrop::JOBINFO jobInfo;
  1009. //
  1010. // Initialize job info.
  1011. //
  1012. jobInfo.dwJobID = _pPrinter->pData()->GetId( (HITEM)lvi.lParam );
  1013. jobInfo.hwndLV = _hwndLV;
  1014. jobInfo.iItem = lvi.iItem;
  1015. _pPrinter->pszPrinterName(ARRAYSIZE(jobInfo.szPrinterName), jobInfo.szPrinterName);
  1016. //
  1017. // Kick off OLE2 drag & drop.
  1018. //
  1019. CRefPtrCOM<IDataObject> spDataObj;
  1020. CRefPtrCOM<IDropSource> spDropSrc;
  1021. if( SUCCEEDED(DragDrop::CreatePrintJobObject(jobInfo, IID_IDataObject, (void **)&spDataObj)) &&
  1022. SUCCEEDED(spDataObj->QueryInterface(IID_IDropSource, (void **)&spDropSrc)) )
  1023. {
  1024. DWORD dwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY;
  1025. SHDoDragDrop(_hwndLV, spDataObj, spDropSrc, dwEffect, &dwEffect);
  1026. }
  1027. }
  1028. return 0;
  1029. }
  1030. VOID
  1031. TQueue::
  1032. vInitPrinterMenu(
  1033. HMENU hMenu
  1034. )
  1035. {
  1036. //
  1037. // If printer is paused, enable pause, etc.
  1038. //
  1039. BOOL bPaused = _dwStatusPrinter & PRINTER_STATUS_PAUSED;
  1040. //
  1041. // Disable admin functions if not an administrator.
  1042. // We should guard this, but since it's just status, don't bother.
  1043. //
  1044. BOOL bAdministrator = _pPrinter->dwAccess() == PRINTER_ALL_ACCESS;
  1045. BOOL bDirect = _dwAttributes & PRINTER_ATTRIBUTE_DIRECT ? TRUE : FALSE;
  1046. TCHAR szPrinterBuffer[kPrinterBufMax];
  1047. LPTSTR pszPrinter;
  1048. pszPrinter = _pPrinter->pszPrinterName( ARRAYSIZE(szPrinterBuffer), szPrinterBuffer );
  1049. TCHAR szScratch[2];
  1050. // let's do a quick EnumPrinters call here and see if this printer is
  1051. // locally installed (local printer or printer connection)
  1052. BOOL bInstalled = FALSE;
  1053. DWORD cPrinters = 0, cbBuffer = 0;
  1054. DWORD dwIndex = static_cast<DWORD>(-1);
  1055. CAutoPtrSpl<PRINTER_INFO_5> spPrinters;
  1056. if( VDataRefresh::bEnumPrinters(PRINTER_ENUM_CONNECTIONS|PRINTER_ENUM_LOCAL,
  1057. NULL, 5, (void **)&spPrinters, &cbBuffer, &cPrinters) )
  1058. {
  1059. // linear search to see if our printer is installed locally,
  1060. for( DWORD dw = 0; dw < cPrinters; dw++ )
  1061. {
  1062. if( 0 == lstrcmp(spPrinters[dw].pPrinterName, pszPrinter) )
  1063. {
  1064. dwIndex = dw;
  1065. bInstalled = TRUE;
  1066. break;
  1067. }
  1068. }
  1069. }
  1070. BOOL bDefault = bInstalled ? CheckDefaultPrinter(pszPrinter) == kDefault : FALSE;
  1071. BOOL bIsLocal = bInstalled ? _pPrinter->pszServerName(ARRAYSIZE(szPrinterBuffer), szPrinterBuffer) == NULL : FALSE;
  1072. BOOL bIsNowOffline = _dwAttributes & PRINTER_ATTRIBUTE_WORK_OFFLINE;
  1073. CheckMenuItem( hMenu,
  1074. IDM_PRINTER_SET_DEFAULT,
  1075. bDefault ?
  1076. MF_BYCOMMAND|MF_CHECKED :
  1077. MF_BYCOMMAND|MF_UNCHECKED );
  1078. EnableMenuItem( hMenu,
  1079. IDM_PRINTER_SET_DEFAULT,
  1080. bInstalled ?
  1081. MF_BYCOMMAND|MF_ENABLED :
  1082. MF_BYCOMMAND|MF_DISABLED|MF_GRAYED );
  1083. EnableMenuItem( hMenu,
  1084. IDM_PRINTER_INSTALL,
  1085. bInstalled ?
  1086. MF_BYCOMMAND|MF_DISABLED|MF_GRAYED :
  1087. MF_BYCOMMAND|MF_ENABLED );
  1088. CheckMenuItem( hMenu,
  1089. IDM_PRINTER_PAUSE,
  1090. bPaused ?
  1091. MF_BYCOMMAND|MF_CHECKED :
  1092. MF_BYCOMMAND|MF_UNCHECKED );
  1093. EnableMenuItem( hMenu,
  1094. IDM_PRINTER_PAUSE,
  1095. bAdministrator ?
  1096. MF_BYCOMMAND|MF_ENABLED :
  1097. MF_BYCOMMAND|MF_DISABLED|MF_GRAYED );
  1098. EnableMenuItem( hMenu,
  1099. IDM_PRINTER_PURGE,
  1100. bAdministrator ?
  1101. MF_BYCOMMAND|MF_ENABLED :
  1102. MF_BYCOMMAND|MF_DISABLED|MF_GRAYED );
  1103. CheckMenuItem( hMenu,
  1104. IDM_PRINTER_WORKOFFLINE,
  1105. bIsNowOffline ?
  1106. MF_BYCOMMAND|MF_CHECKED :
  1107. MF_BYCOMMAND|MF_UNCHECKED );
  1108. EnableMenuItem( hMenu,
  1109. IDM_PRINTER_WORKOFFLINE,
  1110. bAdministrator ?
  1111. MF_BYCOMMAND|MF_ENABLED :
  1112. MF_BYCOMMAND|MF_DISABLED|MF_GRAYED );
  1113. BOOL bIsRedirected = FALSE;
  1114. BOOL bIsMasq = _dwAttributes & PRINTER_ATTRIBUTE_LOCAL && _dwAttributes & PRINTER_ATTRIBUTE_NETWORK;
  1115. //
  1116. // We only check for redirected port for local printer.
  1117. // If bIsLocal is TRUE, bInstalled must be TRUE
  1118. //
  1119. if( bIsLocal && (dwIndex != static_cast<DWORD>(-1)) )
  1120. {
  1121. IsRedirectedPort( spPrinters[dwIndex].pPortName, &bIsRedirected );
  1122. }
  1123. //
  1124. // Remove the work offline menu item if this printer is
  1125. // not local or the printer is a masq printer, or if it's a
  1126. // redirected port printer which is not offline now.
  1127. //
  1128. if( !_dwAttributes || !bIsLocal || (bIsRedirected && !bIsNowOffline) || bIsMasq )
  1129. {
  1130. RemoveMenu( hMenu, IDM_PRINTER_WORKOFFLINE, MF_BYCOMMAND );
  1131. }
  1132. }
  1133. VOID
  1134. TQueue::
  1135. vInitDocMenu(
  1136. BOOL bAllowModify,
  1137. HMENU hMenu
  1138. )
  1139. /*++
  1140. Routine Name:
  1141. vInitDocMenu
  1142. Routine Description:
  1143. Enables or disable the document menu selections.
  1144. Arguments:
  1145. bAllowModify - whether we allow deleting the menu item.
  1146. HMENU - Handle to document menu
  1147. Return Value:
  1148. --*/
  1149. {
  1150. INT iSel = ListView_GetNextItem( _hwndLV, -1, LVNI_SELECTED );
  1151. INT iSelCount = ListView_GetSelectedCount( _hwndLV );
  1152. UINT fuFlags = (iSelCount > 1 || iSel >= 0) ?
  1153. MF_BYCOMMAND|MF_ENABLED :
  1154. MF_BYCOMMAND|MF_DISABLED|MF_GRAYED;
  1155. EnableMenuItem(hMenu, IDM_JOB_PAUSE, fuFlags);
  1156. EnableMenuItem(hMenu, IDM_JOB_RESUME, fuFlags);
  1157. EnableMenuItem(hMenu, IDM_JOB_RESTART, fuFlags);
  1158. EnableMenuItem(hMenu, IDM_JOB_CANCEL, fuFlags);
  1159. EnableMenuItem(hMenu, IDM_JOB_PROPERTIES, fuFlags);
  1160. //
  1161. // If more than one item are selected, we will enable all menu items.
  1162. //
  1163. if( iSelCount == 1 && iSel >= 0 && _pPrinter->pData() )
  1164. {
  1165. // let's see if we can un-clutter the menu a little bit...
  1166. LVITEM lvi;
  1167. ZeroMemory(&lvi, sizeof(lvi));
  1168. lvi.iItem = iSel;
  1169. lvi.mask = LVIF_PARAM;
  1170. if( ListView_GetItem(_hwndLV, &lvi) )
  1171. {
  1172. DWORD dwStatus = _pPrinter->pData()->GetInfo(
  1173. reinterpret_cast<HITEM>(lvi.lParam), TDataNJob::kIndexStatus).dwData;
  1174. if( bAllowModify )
  1175. {
  1176. // delete the corresponding menu item
  1177. DeleteMenu(hMenu, (dwStatus & JOB_STATUS_PAUSED) ? IDM_JOB_PAUSE : IDM_JOB_RESUME, MF_BYCOMMAND);
  1178. }
  1179. else
  1180. {
  1181. // disable the corresponding menu item
  1182. EnableMenuItem(hMenu,
  1183. (dwStatus & JOB_STATUS_PAUSED) ? IDM_JOB_PAUSE : IDM_JOB_RESUME,
  1184. MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
  1185. }
  1186. }
  1187. }
  1188. }
  1189. VOID
  1190. TQueue::
  1191. vInitViewMenu(
  1192. HMENU hMenu
  1193. )
  1194. {
  1195. CheckMenuItem( hMenu,
  1196. IDM_STATUS_BAR,
  1197. _bStatusBar ?
  1198. MF_BYCOMMAND | MF_CHECKED :
  1199. MF_BYCOMMAND | MF_UNCHECKED );
  1200. }
  1201. /********************************************************************
  1202. Support double click context menus.
  1203. ********************************************************************/
  1204. LRESULT
  1205. TQueue::
  1206. lrOnLVDoubleClick(
  1207. VOID
  1208. )
  1209. {
  1210. //
  1211. // We only handle when an item is selected.
  1212. //
  1213. if( ListView_GetNextItem( _hwndLV, -1, LVNI_SELECTED ) < 0 )
  1214. return FALSE;
  1215. //
  1216. // Prevent the selection of multiple jobs
  1217. //
  1218. //
  1219. // If multiple job selections then error.
  1220. //
  1221. if( ListView_GetSelectedCount( _hwndLV ) > 1){
  1222. return FALSE;
  1223. }
  1224. //
  1225. // Display the selected job property.
  1226. //
  1227. vProcessItemCommand( IDM_JOB_PROPERTIES );
  1228. return TRUE;
  1229. }
  1230. /********************************************************************
  1231. Support right click context menus.
  1232. ********************************************************************/
  1233. LRESULT
  1234. TQueue::
  1235. lrOnLVRClick(
  1236. NMHDR* pnmhdr
  1237. )
  1238. {
  1239. LRESULT lReturn = TRUE;
  1240. switch( pnmhdr->code ){
  1241. case NM_RCLICK:
  1242. {
  1243. INT iSel;
  1244. HMENU hmContext;
  1245. POINT pt;
  1246. iSel = ListView_GetNextItem( _hwndLV, -1, LVNI_SELECTED );
  1247. hmContext = ShellServices::LoadPopupMenu(ghInst, MENU_PRINTQUEUE, iSel >= 0 ? 1 : 0);
  1248. if( !hmContext ){
  1249. break;
  1250. }
  1251. if( iSel < 0 ){
  1252. //
  1253. // We need to remove the "Close" menu item
  1254. // (and separator).
  1255. //
  1256. iSel = GetMenuItemCount( hmContext ) - 2;
  1257. DeleteMenu( hmContext, iSel, MF_BYPOSITION );
  1258. DeleteMenu( hmContext, iSel, MF_BYPOSITION );
  1259. vInitPrinterMenu( hmContext );
  1260. } else {
  1261. vInitDocMenu( TRUE, hmContext );
  1262. }
  1263. DWORD dw = GetMessagePos();
  1264. pt.x = GET_X_LPARAM(dw);
  1265. pt.y = GET_Y_LPARAM(dw);
  1266. //
  1267. // The command will just get stuck in the regular queue and
  1268. // handled at that time.
  1269. //
  1270. TrackPopupMenu( hmContext,
  1271. TPM_LEFTALIGN|TPM_RIGHTBUTTON,
  1272. pt.x, pt.y,
  1273. 0, _hwnd, NULL);
  1274. DestroyMenu(hmContext);
  1275. break;
  1276. }
  1277. default:
  1278. lReturn = FALSE;
  1279. break;
  1280. }
  1281. return lReturn;
  1282. }
  1283. /********************************************************************
  1284. Commands.
  1285. ********************************************************************/
  1286. LRESULT
  1287. TQueue::
  1288. lrProcessCommand(
  1289. IN UINT uCommand
  1290. )
  1291. /*++
  1292. Routine Description:
  1293. Process an IDM_* command.
  1294. Arguments:
  1295. Return Value:
  1296. LRESULT
  1297. --*/
  1298. {
  1299. //
  1300. // Item (printer) command.
  1301. //
  1302. if( uCommand >= IDM_PRINTER_COMMAND_FIRST && uCommand <= IDM_PRINTER_COMMAND_LAST )
  1303. {
  1304. TSelection* pSelection = new TSelection( this, _pPrinter );
  1305. if( pSelection )
  1306. {
  1307. switch( uCommand ){
  1308. case IDM_PRINTER_PAUSE:
  1309. pSelection->_CommandType = TSelection::kCommandTypePrinter;
  1310. pSelection->_dwCommandAction = _dwStatusPrinter & PRINTER_STATUS_PAUSED ?
  1311. PRINTER_CONTROL_RESUME :
  1312. PRINTER_CONTROL_PAUSE;
  1313. break;
  1314. case IDM_PRINTER_PURGE:
  1315. {
  1316. TCHAR szScratch[kStrMax+kPrinterBufMax] = {0};
  1317. if( CommandConfirmationPurge(_hwnd, _pPrinter->pszPrinterName(ARRAYSIZE(szScratch), szScratch)) )
  1318. {
  1319. pSelection->_CommandType = TSelection::kCommandTypePrinter;
  1320. pSelection->_dwCommandAction = PRINTER_CONTROL_PURGE;
  1321. }
  1322. }
  1323. break;
  1324. case IDM_PRINTER_WORKOFFLINE:
  1325. pSelection->_CommandType = TSelection::kCommandTypePrinterAttributes;
  1326. pSelection->_dwCommandAction = _dwAttributes ^ PRINTER_ATTRIBUTE_WORK_OFFLINE;
  1327. DBGMSG( DBG_WARN, ( "Queue.lrProcessCommand: Workoffline %d\n", uCommand ));
  1328. break;
  1329. default:
  1330. pSelection->_dwCommandAction = 0;
  1331. DBGMSG( DBG_WARN, ( "Queue.lrProcessCommand: unknown command %d\n", uCommand ));
  1332. break;
  1333. }
  1334. //
  1335. // Queue the async command.
  1336. //
  1337. _pPrinter->vCommandQueue( pSelection );
  1338. }
  1339. else
  1340. {
  1341. vShowResourceError( _hwnd );
  1342. }
  1343. }
  1344. else
  1345. {
  1346. //
  1347. // Item (job) command.
  1348. //
  1349. vProcessItemCommand( uCommand );
  1350. }
  1351. return 0;
  1352. }
  1353. VOID
  1354. TQueue::
  1355. vProcessItemCommand(
  1356. IN UINT uCommand
  1357. )
  1358. /*++
  1359. Routine Description:
  1360. Retrieves all selected items and attemps to execute a command
  1361. on them.
  1362. Arguments:
  1363. uCommand - IDM_* command.
  1364. Return Value:
  1365. --*/
  1366. {
  1367. //
  1368. // Declare job menu id to Job command mapping structure.
  1369. //
  1370. static struct {
  1371. UINT idmCommand;
  1372. DWORD dwCommand;
  1373. } aJobCommand[] = {
  1374. { IDM_JOB_CANCEL, JOB_CONTROL_DELETE },
  1375. { IDM_JOB_PAUSE, JOB_CONTROL_PAUSE },
  1376. { IDM_JOB_RESUME, JOB_CONTROL_RESUME },
  1377. { IDM_JOB_RESTART, JOB_CONTROL_RESTART }
  1378. };
  1379. //
  1380. // Get a list of selected Job IDs
  1381. //
  1382. TSelection* pSelection = new TSelection( this,
  1383. _pPrinter );
  1384. //
  1385. // Check for allocation error. We want to put a pop-up on all
  1386. // user actions if we can detect the error immediately after
  1387. // the user issues the command. Otherwise put in status bar.
  1388. //
  1389. if( !VALID_PTR(pSelection) ){
  1390. vShowResourceError( _hwnd );
  1391. goto NoCommand;
  1392. }
  1393. //
  1394. // There a few job related menu selections which are not
  1395. // deferable events i.e Job properties, This event will be done
  1396. // immediately and then release the selection object.
  1397. //
  1398. switch( uCommand ){
  1399. case IDM_JOB_PROPERTIES: {
  1400. TCHAR szPrinter[kPrinterBufMax];
  1401. LPTSTR pszPrinter;
  1402. pszPrinter = _pPrinter->pszPrinterName( ARRAYSIZE(szPrinter), szPrinter );
  1403. vDocumentPropSelections( NULL, pszPrinter, pSelection );
  1404. goto NoCommand;
  1405. }
  1406. default:
  1407. break;
  1408. }
  1409. //
  1410. // Ask for confirmation...
  1411. //
  1412. if( IDM_JOB_CANCEL == uCommand &&
  1413. IDYES != iMessage(_hwnd, IDS_PRINTERS_TITLE, IDS_QUEUE_SURE_CANCEL,
  1414. MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2, kMsgNone, NULL) )
  1415. {
  1416. goto NoCommand;
  1417. }
  1418. //
  1419. // Map the job menu id to a job command.
  1420. //
  1421. UINT uIndex;
  1422. for( uIndex = 0; uIndex < COUNTOF( aJobCommand ); ++uIndex ){
  1423. //
  1424. // Check for a matching IDM_JOB -> JOB_CONTROL mapping.
  1425. //
  1426. if( aJobCommand[uIndex].idmCommand == uCommand ){
  1427. //
  1428. // Update the command action and job type
  1429. //
  1430. pSelection->_dwCommandAction = aJobCommand[uIndex].dwCommand;
  1431. pSelection->_CommandType = TSelection::kCommandTypeJob;
  1432. //
  1433. // Queue the job commands
  1434. //
  1435. _pPrinter->vCommandQueue( pSelection );
  1436. return;
  1437. }
  1438. }
  1439. //
  1440. // No matches; punt.
  1441. //
  1442. SPLASSERT( FALSE );
  1443. NoCommand:
  1444. delete pSelection;
  1445. return;
  1446. }
  1447. /********************************************************************
  1448. Utils.
  1449. ********************************************************************/
  1450. BOOL
  1451. TQueue::
  1452. bInsertItem(
  1453. HITEM hItem,
  1454. LIST_INDEX ListIndex
  1455. )
  1456. {
  1457. LV_ITEM item;
  1458. //
  1459. // Insert item into listview.
  1460. //
  1461. item.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  1462. item.iSubItem = 0;
  1463. item.pszText = LPSTR_TEXTCALLBACK;
  1464. item.state = 0;
  1465. item.iImage = 0;
  1466. item.iItem = ListIndex;
  1467. item.lParam = (LPARAM)hItem;
  1468. if( ListView_InsertItem( _hwndLV, &item ) < 0 ){
  1469. DBGMSG( DBG_WARN,
  1470. ( "Queue.bInsertItem: Failed to add job %d\n",
  1471. GetLastError( )));
  1472. return FALSE;
  1473. }
  1474. return TRUE;
  1475. }
  1476. /********************************************************************
  1477. Job block processing.
  1478. ********************************************************************/
  1479. VOID
  1480. TQueue::
  1481. vBlockProcess(
  1482. VOID
  1483. )
  1484. /*++
  1485. Routine Description:
  1486. The request has been PostMessage'd and now is ready for
  1487. processing. If we need to save state, get the queue
  1488. selections then notify the pData that something has changed.
  1489. (The pData may call back and request that the list view
  1490. be cleared and reset.)
  1491. Called from UI thread only.
  1492. Arguments:
  1493. Return Value:
  1494. --*/
  1495. {
  1496. SINGLETHREAD(UIThread);
  1497. SPLASSERT( _pPrinter );
  1498. SPLASSERT( _pPrinter->pData( ));
  1499. //
  1500. // Keep track if the number of jobs changes. If it does,
  1501. // then we need to update the status bar.
  1502. //
  1503. COUNT cItems = _cItems;
  1504. //
  1505. // Process all pending blocks.
  1506. //
  1507. _pPrinter->pData()->vBlockProcess();
  1508. if( cItems != _cItems ){
  1509. TCHAR szScratch[kStrMax];
  1510. TCHAR szText[kStrMax];
  1511. szText[0] = 0;
  1512. //
  1513. // Always update the job count.
  1514. //
  1515. if( LoadString( ghInst,
  1516. IDS_SB_JOBS,
  1517. szScratch,
  1518. COUNTOF( szScratch ))){
  1519. StringCchPrintf( szText, ARRAYSIZE(szText), szScratch, _cItems );
  1520. }
  1521. SendMessage( _hwndSB,
  1522. SB_SETTEXT,
  1523. kStatusPaneJobs,
  1524. (LPARAM)szText );
  1525. //
  1526. // Check if it's pending deletion and we just printed the
  1527. // last job. The queue window should close.
  1528. //
  1529. bDeletingAndNoJobs();
  1530. }
  1531. }
  1532. /********************************************************************
  1533. Private status helper functions
  1534. ********************************************************************/
  1535. LPTSTR
  1536. TQueue::
  1537. pszStatusString(
  1538. OUT LPTSTR pszDest,
  1539. IN OUT UINT& cchMark,
  1540. IN DWORD dwStatus,
  1541. IN BOOL bInitialSep,
  1542. IN BOOL bFirstOnly,
  1543. IN const STATUS_MAP pStatusMaps[]
  1544. )
  1545. /*++
  1546. Routine Description:
  1547. Builds a status string into pszDest, based on the dwStatus bitfield
  1548. and Type.
  1549. Arguments:
  1550. pszDest - Buffer to receive status string.
  1551. cchMark - Char count of pszDest; on return, holds chars remaining.
  1552. dwStatus - DWORD status field matching Type.
  1553. bInitialSep - Indicates whether an initial separator is needed.
  1554. pStatusMaps - Pointer to array of status maps (bit -> IDS).
  1555. bFirstOnly - Adds only 1 status string.
  1556. Return Value:
  1557. Pointer to the end of the string (ready for next vStrCat.
  1558. cchMark is updated.
  1559. --*/
  1560. {
  1561. TCHAR szStatus[kStrMax];
  1562. LPTSTR pszMark = pszDest;
  1563. UINT i;
  1564. for( i = 0, pszMark[0] = 0;
  1565. pStatusMaps->dwMask;
  1566. ++i, ++pStatusMaps ){
  1567. if( pStatusMaps->dwMask & dwStatus ){
  1568. if( !LoadString( ghInst,
  1569. pStatusMaps->uIDS,
  1570. szStatus,
  1571. COUNTOF( szStatus ))){
  1572. DBGMSG( DBG_ERROR,
  1573. ( "Queue.pszStatusString: unable to load %d, error %d\n",
  1574. pStatusMaps->uIDS,
  1575. GetLastError( )));
  1576. continue;
  1577. }
  1578. //
  1579. // If not at the beginning, we need a separator.
  1580. //
  1581. if( pszMark != pszDest || bInitialSep ){
  1582. //
  1583. // Spit out a separator.
  1584. //
  1585. pszMark = pszStrCat( pszMark,
  1586. gszStatusSeparator,
  1587. cchMark );
  1588. if( !pszMark )
  1589. break;
  1590. }
  1591. //
  1592. // Append the status string.
  1593. //
  1594. pszMark = pszStrCat( pszMark,
  1595. szStatus,
  1596. cchMark );
  1597. if( !pszMark || bFirstOnly ){
  1598. break;
  1599. }
  1600. }
  1601. }
  1602. return pszMark;
  1603. }
  1604. /********************************************************************
  1605. MPrinterClient virtual definitions.
  1606. ********************************************************************/
  1607. VOID
  1608. TQueue::
  1609. vItemChanged(
  1610. IN ITEM_CHANGE ItemChange,
  1611. IN HITEM hItem,
  1612. IN INFO Info,
  1613. IN INFO InfoNew
  1614. )
  1615. /*++
  1616. Routine Description:
  1617. A particular item changed, refresh just part of the window.
  1618. Note: Currently there is no sorting, so the NATURAL_INDEX is
  1619. the same as the LIST_INDEX.
  1620. When a TData* calls this routine, it must also upate its data
  1621. structures before calling this.
  1622. Arguments:
  1623. ItemChange - Indicates what about the job changed.
  1624. hItem - Handle to job that changed.
  1625. Info - Depends on the type of change; generally the old version
  1626. of the info.
  1627. InfoNew - Depends on the type of change; generally the new version
  1628. of the info.
  1629. Return Value:
  1630. --*/
  1631. {
  1632. SINGLETHREAD(UIThread);
  1633. //
  1634. // Fix up one job.
  1635. //
  1636. switch( ItemChange ){
  1637. case kItemCreate:
  1638. //
  1639. // Always insert at the end of the list.
  1640. //
  1641. Info.NaturalIndex = _cItems;
  1642. //
  1643. // How to handle this error?
  1644. //
  1645. // !! SORT !!
  1646. // iItem == NaturalIndex only if no sorting is enabled.
  1647. //
  1648. bInsertItem( hItem, Info.NaturalIndex );
  1649. ++_cItems;
  1650. break;
  1651. case kItemDelete:
  1652. //
  1653. // Delete the item from the listview.
  1654. //
  1655. //
  1656. // !! SORT !!
  1657. // iItem == NaturalIndex only if no sorting is enabled.
  1658. //
  1659. if( !bDeleteItem( Info.NaturalIndex )){
  1660. DBGMSG( DBG_WARN,
  1661. ( "Queue.vItemChanged: Failed to del job %d\n",
  1662. GetLastError( )));
  1663. }
  1664. --_cItems;
  1665. break;
  1666. case kItemName:
  1667. //
  1668. // We must set the item text, or else the width of the
  1669. // label doesn't change.
  1670. //
  1671. //
  1672. // !! SORT !!
  1673. //
  1674. ListView_SetItemText(
  1675. _hwndLV,
  1676. Info.NaturalIndex,
  1677. 0,
  1678. (LPTSTR)_pPrinter->pData()->GetInfo( hItem, 0 ).pszData );
  1679. //
  1680. // Fall through.
  1681. //
  1682. case kItemInfo:
  1683. case kItemAttributes:
  1684. //
  1685. // If it's visible, invalidate the line.
  1686. //
  1687. //
  1688. // !! SORT !!
  1689. // iItem == NaturalIndex only if no sorting is enabled.
  1690. //
  1691. RECT rc;
  1692. if( ListView_GetItemRect( _hwndLV,
  1693. Info.NaturalIndex,
  1694. &rc,
  1695. LVIR_BOUNDS )){
  1696. InvalidateRect( _hwndLV, &rc, TRUE );
  1697. }
  1698. break;
  1699. case kItemPosition:
  1700. vItemPositionChanged( hItem,
  1701. Info.NaturalIndex,
  1702. InfoNew.NaturalIndex );
  1703. break;
  1704. default:
  1705. DBGMSG( DBG_ERROR,
  1706. ( "Queue.vItemChanged: Unknown change %x\n", ItemChange ));
  1707. break;
  1708. }
  1709. }
  1710. VOID
  1711. TQueue::
  1712. vContainerChanged(
  1713. IN CONTAINER_CHANGE ContainerChange,
  1714. IN INFO Info
  1715. )
  1716. {
  1717. DBGMSG( DBG_QUEUEINFO,
  1718. ( "Queue.vContainerChanged: %x %x\n", ContainerChange, Info.dwData ));
  1719. //
  1720. // Some of the commands are synchronous. Handle them first.
  1721. //
  1722. switch( ContainerChange ){
  1723. case kContainerReloadItems:
  1724. vReloadItems( Info.dwData );
  1725. break;
  1726. case kContainerClearItems:
  1727. vClearItems();
  1728. break;
  1729. case kContainerStateVar:
  1730. _pPrintLib->bJobAdd( _pPrinter, Info.dwData );
  1731. break;
  1732. default:
  1733. //
  1734. // All asynchronous commands use PostMessage to get to UI thread.
  1735. //
  1736. PostMessage( _hwnd, WM_PRINTLIB_STATUS, ContainerChange, Info.dwData );
  1737. break;
  1738. }
  1739. }
  1740. VOID
  1741. TQueue::
  1742. vSaveSelections(
  1743. VOID
  1744. )
  1745. {
  1746. SINGLETHREAD(UIThread);
  1747. //
  1748. // State needs to be saved.
  1749. // NOT RE-ENTRANT.
  1750. //
  1751. SPLASSERT( !SaveSelection._pSelection );
  1752. //
  1753. // Determine which item is selected and store the Id.
  1754. //
  1755. SaveSelection._idFocused = kInvalidIdentValue;
  1756. INT iItem = ListView_GetNextItem( _hwndLV, -1, LVNI_FOCUSED );
  1757. if( iItem != -1 ){
  1758. //
  1759. // !! SORTORDER !!
  1760. //
  1761. HANDLE hItem = _pPrinter->pData()->GetItem( iItem );
  1762. SaveSelection._idFocused = _pPrinter->pData()->GetId( hItem );
  1763. }
  1764. //
  1765. // Save selected Items.
  1766. //
  1767. // Can't handle a failure here--we don't want to pop up a
  1768. // message box (since this could be poll-refresh) and we
  1769. // don't want to disturb the current error in the status bar.
  1770. // (The status bar error is only for user-initiated commands:
  1771. // we assume the user will look here before executing another
  1772. // commnad.)
  1773. //
  1774. SaveSelection._pSelection = new TSelection( this,
  1775. _pPrinter );
  1776. }
  1777. VOID
  1778. TQueue::
  1779. vRestoreSelections(
  1780. VOID
  1781. )
  1782. {
  1783. SINGLETHREAD(UIThread);
  1784. if( SaveSelection._idFocused != kInvalidIdentValue ){
  1785. NATURAL_INDEX NaturalIndex;
  1786. LV_ITEM item;
  1787. //
  1788. // Translate ID to DataIndex.
  1789. // !! SORT ORDER !!
  1790. //
  1791. item.stateMask =
  1792. item.state = LVIS_FOCUSED;
  1793. NaturalIndex = _pPrinter->pData()->GetNaturalIndex( SaveSelection._idFocused,
  1794. NULL );
  1795. //
  1796. // The DataIndex value may be gone of the selectd Item was
  1797. // deleted or printed.
  1798. //
  1799. if( NaturalIndex != kInvalidNaturalIndexValue ){
  1800. SendMessage( _hwndLV,
  1801. LVM_SETITEMSTATE,
  1802. NaturalIndex,
  1803. (LPARAM)&item );
  1804. }
  1805. }
  1806. //
  1807. // Don't check using VALID_PTR since the no-selection case will
  1808. // cause it to fail, but we don't want to flag an error.
  1809. //
  1810. if( SaveSelection._pSelection && SaveSelection._pSelection->bValid( )){
  1811. NATURAL_INDEX NaturalIndex;
  1812. COUNT i;
  1813. PIDENT pid;
  1814. LV_ITEM item;
  1815. item.stateMask =
  1816. item.state = LVIS_SELECTED;
  1817. for( i = 0, pid = SaveSelection._pSelection->_pid;
  1818. i < SaveSelection._pSelection->_cSelected;
  1819. ++i, ++pid ){
  1820. //
  1821. // Translate IDENT to DataIndex.
  1822. // !! SORT ORDER !!
  1823. //
  1824. NaturalIndex = _pPrinter->pData()->GetNaturalIndex( *pid,
  1825. NULL );
  1826. //
  1827. // The DataIndex value may be gone of the selected Item was
  1828. // deleted or printed.
  1829. //
  1830. if( NaturalIndex != kInvalidNaturalIndexValue ){
  1831. SendMessage( _hwndLV,
  1832. LVM_SETITEMSTATE,
  1833. NaturalIndex,
  1834. (LPARAM)&item );
  1835. }
  1836. }
  1837. }
  1838. //
  1839. // Cleanup even if we fail.
  1840. //
  1841. delete SaveSelection._pSelection;
  1842. SaveSelection._pSelection = NULL;
  1843. }
  1844. VDataNotify*
  1845. TQueue::
  1846. pNewNotify(
  1847. MDataClient* pDataClient
  1848. ) const
  1849. {
  1850. return new TDataNJob( pDataClient );
  1851. }
  1852. VDataRefresh*
  1853. TQueue::
  1854. pNewRefresh(
  1855. MDataClient* pDataClient
  1856. ) const
  1857. {
  1858. return new TDataRJob( pDataClient );
  1859. }
  1860. /********************************************************************
  1861. Retrieve selected items. Used when processing commands against
  1862. items or saving and restoring the selection during a refresh.
  1863. ********************************************************************/
  1864. COUNT
  1865. TQueue::
  1866. cSelected(
  1867. VOID
  1868. ) const
  1869. {
  1870. SINGLETHREAD(UIThread);
  1871. return ListView_GetSelectedCount( _hwndLV );
  1872. }
  1873. HANDLE
  1874. TQueue::
  1875. GetFirstSelItem(
  1876. VOID
  1877. ) const
  1878. {
  1879. SINGLETHREAD(UIThread);
  1880. INT iItem = ListView_GetNextItem( _hwndLV,
  1881. -1,
  1882. LVNI_SELECTED );
  1883. return INT2PTR(iItem, HANDLE);
  1884. }
  1885. HANDLE
  1886. TQueue::
  1887. GetNextSelItem(
  1888. HANDLE hItem
  1889. ) const
  1890. {
  1891. SINGLETHREAD(UIThread);
  1892. INT iJob = (INT)(ULONG_PTR)hItem;
  1893. SPLASSERT( iJob < 0x8000 );
  1894. iJob = ListView_GetNextItem( _hwndLV,
  1895. iJob,
  1896. LVNI_SELECTED );
  1897. if( iJob == -1 ){
  1898. DBGMSG( DBG_ERROR,
  1899. ( "Queue.hItemNext: LV_GNI failed %d\n",
  1900. GetLastError( )));
  1901. }
  1902. return INT2PTR(iJob, HANDLE);
  1903. }
  1904. IDENT
  1905. TQueue::
  1906. GetId(
  1907. HANDLE hItem
  1908. ) const
  1909. {
  1910. SINGLETHREAD(UIThread);
  1911. INT iJob = (INT)(ULONG_PTR)hItem;
  1912. if( iJob != -1 ){
  1913. hItem = _pPrinter->pData()->GetItem( iJob );
  1914. return _pPrinter->pData()->GetId( hItem );
  1915. }
  1916. return kInvalidIdentValue;
  1917. }
  1918. BOOL
  1919. TQueue::
  1920. bGetPrintLib(
  1921. TRefLock<TPrintLib> &refLock
  1922. ) const
  1923. {
  1924. ASSERT(_pPrintLib.pGet());
  1925. if (_pPrintLib.pGet())
  1926. {
  1927. refLock.vAcquire(_pPrintLib.pGet());
  1928. return TRUE;
  1929. }
  1930. return FALSE;
  1931. }
  1932. VOID
  1933. TQueue::
  1934. vRefZeroed(
  1935. VOID
  1936. )
  1937. {
  1938. if( bValid( )){
  1939. delete this;
  1940. }
  1941. }
  1942. /********************************************************************
  1943. Implementation functions.
  1944. ********************************************************************/
  1945. VOID
  1946. TQueue::
  1947. vContainerChangedHandler(
  1948. IN CONTAINER_CHANGE ContainerChange,
  1949. IN INFO Info
  1950. )
  1951. {
  1952. static DWORD gadwConnectStatusMap[] = CONNECT_STATUS_MAP;
  1953. SINGLETHREAD(UIThread);
  1954. switch( ContainerChange ){
  1955. case kContainerNewBlock:
  1956. vBlockProcess();
  1957. break;
  1958. case kContainerAttributes:
  1959. _dwAttributes = Info.dwData;
  1960. //
  1961. // Update the queue view title, (Note status information is displayed here)
  1962. //
  1963. vUpdateTitle();
  1964. //
  1965. // !! LATER !!
  1966. //
  1967. // Change printer icon.
  1968. //
  1969. break;
  1970. case kContainerName:
  1971. //
  1972. // Update the queue view title, (Note status information is displayed here)
  1973. //
  1974. vUpdateTitle();
  1975. break;
  1976. case kContainerStatus:
  1977. _dwStatusPrinter = Info.dwData;
  1978. //
  1979. // If the printer is pending deletion and has no jobs,
  1980. // then immediately punt.
  1981. //
  1982. if( bDeletingAndNoJobs( )){
  1983. return;
  1984. }
  1985. //
  1986. // Update the queue view title, (Note status information is displayed here)
  1987. //
  1988. vUpdateTitle();
  1989. break;
  1990. case kContainerConnectStatus:
  1991. SPLASSERT( Info.dwData < COUNTOF( gadwConnectStatusMap ));
  1992. _idsConnectStatus = gadwConnectStatusMap[Info.dwData];
  1993. //
  1994. // If the printer isn't found, then put up a message box and
  1995. // dismiss the queue view.
  1996. //
  1997. if( Info.dwData == kConnectStatusInvalidPrinterName ){
  1998. TCHAR szPrinter[kPrinterBufMax];
  1999. LPTSTR pszPrinter = _pPrinter->pszPrinterName( ARRAYSIZE(szPrinter), szPrinter );
  2000. iMessage( _hwnd,
  2001. IDS_ERR_PRINTER_NOT_FOUND_TITLE,
  2002. IDS_ERR_PRINTER_NOT_FOUND,
  2003. MB_OK|MB_ICONSTOP,
  2004. ERROR_INVALID_PRINTER_NAME,
  2005. NULL,
  2006. pszPrinter );
  2007. SendMessage( _hwnd,
  2008. WM_CLOSE,
  2009. 0,
  2010. 0 );
  2011. break;
  2012. }
  2013. //
  2014. // Update the queue view title, (Note status information is displayed here)
  2015. //
  2016. vUpdateTitle();
  2017. break;
  2018. case kContainerErrorStatus: {
  2019. _dwErrorStatus = Info.dwData;
  2020. //
  2021. // Scan for known errors and translate into friendly strings.
  2022. //
  2023. static const ERROR_MAP gaErrorMap[] = {
  2024. ERROR_ACCESS_DENIED, IDS_ERR_ACCESS_DENIED,
  2025. };
  2026. COUNT i;
  2027. DWORD idsError = IDS_SB_ERROR;
  2028. for( i=0; i< COUNTOF( gaErrorMap ); ++i ){
  2029. if( _dwErrorStatus == gaErrorMap[i].dwError ){
  2030. idsError = gaErrorMap[i].uIDS;
  2031. break;
  2032. }
  2033. }
  2034. TString strText;
  2035. if( _dwErrorStatus ){
  2036. TStatusB bStatus;
  2037. bStatus DBGCHK = strText.bLoadString( ghInst, idsError );
  2038. }
  2039. SendMessage( _hwndSB, SB_SETTEXT, kStatusPaneError, (LPARAM)(LPCTSTR)strText );
  2040. break;
  2041. }
  2042. case kContainerRefreshComplete:
  2043. break;
  2044. default:
  2045. DBGMSG( DBG_ERROR, ( "Queue.vContainerChanged: unknown ContainerChange %x\n", ContainerChange ));
  2046. break;
  2047. }
  2048. }
  2049. VOID
  2050. TQueue::
  2051. vUpdateTitle(
  2052. VOID
  2053. )
  2054. {
  2055. TCHAR szScratch[kStrMax+kPrinterBufMax];
  2056. TCHAR szText[kStrMax+kPrinterBufMax];
  2057. UINT nSize = COUNTOF( szText );
  2058. //
  2059. // Build the printer status string.
  2060. //
  2061. ConstructPrinterFriendlyName( pszPrinterName( ARRAYSIZE(szScratch), szScratch ), szText, &nSize );
  2062. //
  2063. // Calculate the string text.
  2064. //
  2065. UINT cch = lstrlen( szText );
  2066. LPTSTR pszText = szText + cch;
  2067. cch = COUNTOF( szText ) - cch;
  2068. //
  2069. // Update the title text with the current printer status.
  2070. //
  2071. pszText = pszStatusString( pszText, cch, _dwStatusPrinter, TRUE, FALSE, gaStatusMapPrinter );
  2072. //
  2073. // Special case the work off line status, because work offline is not
  2074. // implemented in the spooler as a printer status rather as a printer attribute.
  2075. //
  2076. if( _dwAttributes & PRINTER_ATTRIBUTE_WORK_OFFLINE )
  2077. {
  2078. if( LoadString( ghInst, IDS_WORK_OFFLINE, szScratch, COUNTOF( szScratch )))
  2079. {
  2080. pszText = pszStrCat( pszText, gszSpace, cch );
  2081. pszText = pszStrCat( pszText, gszStatusSeparator, cch );
  2082. pszText = pszStrCat( pszText, gszSpace, cch );
  2083. pszText = pszStrCat( pszText, szScratch, cch );
  2084. }
  2085. }
  2086. //
  2087. // If the connection status changed then update the text.
  2088. //
  2089. if( _idsConnectStatus ){
  2090. if( LoadString( ghInst, _idsConnectStatus, szScratch, COUNTOF( szScratch )))
  2091. {
  2092. pszText = pszStrCat( pszText, gszSpace, cch );
  2093. pszText = pszStrCat( pszText, szScratch, cch );
  2094. }
  2095. }
  2096. //
  2097. // Set the queu view title bar text.
  2098. //
  2099. SetWindowText( _hwnd, szText );
  2100. }
  2101. BOOL
  2102. TQueue::
  2103. bDeletingAndNoJobs(
  2104. VOID
  2105. )
  2106. /*++
  2107. Routine Description:
  2108. Returns TRUE if the queue is pending deletion and has no jobs.
  2109. In this case it will also close the window.
  2110. Arguments:
  2111. Return Value:
  2112. TRUE - Pending deletion with no Jobs; window also closed.
  2113. FALSE - Not (pending deletion and no jobs).
  2114. --*/
  2115. {
  2116. if( _cItems == 0 && _dwStatusPrinter & PRINTER_STATUS_PENDING_DELETION ){
  2117. PostMessage( _hwnd, WM_CLOSE, 0, 0 );
  2118. return TRUE;
  2119. }
  2120. return FALSE;
  2121. }
  2122. VOID
  2123. TQueue::
  2124. vItemPositionChanged(
  2125. IN HITEM hItem,
  2126. IN NATURAL_INDEX NaturalIndex,
  2127. IN NATURAL_INDEX NaturalIndexNew
  2128. )
  2129. {
  2130. SPLASSERT( NaturalIndexNew < _cItems );
  2131. SPLASSERT( NaturalIndex != NaturalIndexNew );
  2132. DBGMSG( DBG_QUEUEINFO,
  2133. ( "Queue.vItemPositionChanged: Change requested %d %d %x\n",
  2134. NaturalIndex, NaturalIndexNew, hItem ));
  2135. //
  2136. // Get the item state.
  2137. //
  2138. UINT uState = ListView_GetItemState( _hwndLV,
  2139. NaturalIndex,
  2140. LVIS_FOCUSED | LVIS_SELECTED );
  2141. //
  2142. // Move it to the right place.
  2143. //
  2144. if( !bDeleteItem( NaturalIndex )){
  2145. DBGMSG( DBG_WARN,
  2146. ( "Queue.vItemPositionChanged: Moving, delete failed on %d %d\n",
  2147. NaturalIndex, GetLastError( )));
  2148. }
  2149. if( bInsertItem( hItem, NaturalIndexNew )){
  2150. //
  2151. // Set item state.
  2152. //
  2153. ListView_SetItemState( _hwndLV,
  2154. NaturalIndexNew,
  2155. LVIS_FOCUSED | LVIS_SELECTED,
  2156. uState );
  2157. } else {
  2158. DBGMSG( DBG_ERROR,
  2159. ( "Queue.vItemPositionChanged: Moving, insert failed on %d %d\n",
  2160. NaturalIndex, GetLastError( )));
  2161. }
  2162. }
  2163. VOID
  2164. TQueue::
  2165. vClearItems(
  2166. VOID
  2167. )
  2168. /*++
  2169. Routine Description:
  2170. Removes all items from the list view.
  2171. May be called from either UI or worker thread. If called from
  2172. worker thread, must guarantee that there are no synchronization
  2173. problems.
  2174. Arguments:
  2175. Return Value:
  2176. --*/
  2177. {
  2178. DBGMSG( DBG_QUEUEINFO, ( "Queue.vClearItems: Clearing %d %d\n", _hwndLV, this ));
  2179. TStatusB bStatus;
  2180. //
  2181. // Clear out all items from list view.
  2182. //
  2183. bStatus DBGCHK = ListView_DeleteAllItems( _hwndLV );
  2184. }
  2185. VOID
  2186. TQueue::
  2187. vReloadItems(
  2188. COUNT cItems
  2189. )
  2190. /*++
  2191. Routine Description:
  2192. Delete all items in the list view and refresh based on the
  2193. new pData information.
  2194. Arguments:
  2195. Return Value:
  2196. --*/
  2197. {
  2198. SINGLETHREAD(UIThread);
  2199. DBGMSG( DBG_QUEUEINFO,
  2200. ( "Queue.vReloadItems: Reload %d %d %d\n",
  2201. _hwndLV, this, cItems ));
  2202. vClearItems();
  2203. _cItems = cItems;
  2204. //
  2205. // If we have Items, insert them.
  2206. //
  2207. if( _pPrinter->pData() && cItems ){
  2208. LV_ITEM item;
  2209. //
  2210. // Notify the list view of how many jobs we will ultimately insert
  2211. // to avoid reallocs.
  2212. //
  2213. ListView_SetItemCount( _hwndLV, cItems );
  2214. //
  2215. // Add to the listview.
  2216. //
  2217. item.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  2218. item.iSubItem = 0;
  2219. item.pszText = LPSTR_TEXTCALLBACK;
  2220. item.state = 0;
  2221. item.iImage = 0;
  2222. HANDLE hItem;
  2223. COUNT cItemIndex;
  2224. for( cItemIndex = 0, hItem = _pPrinter->pData()->GetItem( 0 );
  2225. cItemIndex < cItems;
  2226. ++cItemIndex, hItem = _pPrinter->pData()->GetNextItem( hItem )){
  2227. item.iItem = cItemIndex;
  2228. item.lParam = (LPARAM)hItem;
  2229. if( ListView_InsertItem( _hwndLV, &item ) < 0 ){
  2230. DBGMSG( DBG_ERROR,
  2231. ( "Queue.vReloadItems: Failed to insert item %d %d\n",
  2232. item, GetLastError( )));
  2233. break;
  2234. }
  2235. }
  2236. }
  2237. }
  2238. BOOL
  2239. TQueue::
  2240. bOnCopyData(
  2241. IN WPARAM wParam,
  2242. IN LPARAM lParam
  2243. )
  2244. /*++
  2245. Routine Description:
  2246. Get the data pased to it and checks if it matches the
  2247. current printer name.
  2248. Arguments:
  2249. Return Value:
  2250. TRUE printer name matches, FALSE match not found.
  2251. --*/
  2252. {
  2253. TCHAR szFullPrinterName[kPrinterBufMax];
  2254. TCHAR szPrinterName[kPrinterBufMax];
  2255. BOOL bStatus = FALSE;
  2256. PCOPYDATASTRUCT pcpds = (PCOPYDATASTRUCT)lParam;
  2257. LPCTSTR pszPrinterName = (LPTSTR)pcpds->lpData;
  2258. //
  2259. // Check for valid queue signature.
  2260. //
  2261. if( pcpds->dwData == static_cast<DWORD>( TQueue::kQueueSignature ) ){
  2262. //
  2263. // Get the queue view printer name, this may not be fully qualified
  2264. // if it's a local printer.
  2265. //
  2266. if( _pPrinter->pszPrinterName( ARRAYSIZE(szPrinterName), szPrinterName ) ){
  2267. DBGMSG( DBG_TRACE, ("CopyMessage Printer Name " TSTR " Passed Data " TSTR "\n", szPrinterName, pszPrinterName ) );
  2268. //
  2269. // If the printer name passed is fully qualified and the current
  2270. // queue view printer name is not fully qualified then prepend the
  2271. // computer name.
  2272. //
  2273. if( pszPrinterName[0] == TEXT('\\') &&
  2274. pszPrinterName[1] == TEXT('\\') &&
  2275. szPrinterName[0] != TEXT('\\') &&
  2276. szPrinterName[1] != TEXT('\\') ){
  2277. StringCchCopy( szFullPrinterName, ARRAYSIZE(szFullPrinterName), _pPrintLib->strComputerName() );
  2278. StringCchCat( szFullPrinterName, ARRAYSIZE(szFullPrinterName), TEXT("\\") );
  2279. StringCchCat( szFullPrinterName, ARRAYSIZE(szFullPrinterName), szPrinterName );
  2280. //
  2281. // If the passed printer name is not fully qualified and the
  2282. // queue view is fully qualified then strip the computer name.
  2283. //
  2284. } else if( pszPrinterName[0] != TEXT('\\') &&
  2285. pszPrinterName[1] != TEXT('\\') &&
  2286. szPrinterName[0] == TEXT('\\') &&
  2287. szPrinterName[1] == TEXT('\\') ){
  2288. LPCTSTR psz = _tcschr( szPrinterName+2, TEXT('\\') );
  2289. StringCchCopy( szFullPrinterName, ARRAYSIZE(szFullPrinterName), psz ? psz : TEXT("") );
  2290. //
  2291. // The passed printer name and the queue view printer names are either
  2292. // fully qualified or not, but they match.
  2293. //
  2294. } else {
  2295. StringCchCopy( szFullPrinterName, ARRAYSIZE(szFullPrinterName), szPrinterName );
  2296. }
  2297. //
  2298. // Check if the printer names match.
  2299. //
  2300. if( !_tcsicmp( szFullPrinterName, pszPrinterName ) ){
  2301. bStatus = TRUE;
  2302. }
  2303. }
  2304. }
  2305. return bStatus;
  2306. }
  2307. BOOL
  2308. TQueue::
  2309. bIsDuplicateWindow(
  2310. IN HWND hwndOwner,
  2311. IN LPCTSTR pszPrinterName,
  2312. IN HWND *phwnd
  2313. )
  2314. /*++
  2315. Routine Description:
  2316. Asks the queue view window if you are the one with the specified
  2317. printer name. This will ensure there is only one queue view per
  2318. printer on the machine.
  2319. Arguments:
  2320. hwndOwner - Handle of parent window
  2321. pszPrinterName - Printer name to check
  2322. *phwnd - Pointer where to return the duplicate previous window handle.
  2323. Return Value:
  2324. TRUE duplicate found and phwnd point to its window handle.
  2325. FALSE duplicate window not found.
  2326. --*/
  2327. {
  2328. BOOL bStatus = FALSE;
  2329. HWND hwnd = NULL;
  2330. DBGMSG( DBG_TRACE, ("Searching for Printer Name " TSTR "\n", pszPrinterName ) );
  2331. for (hwnd = FindWindow(gszClassName, NULL); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
  2332. {
  2333. TCHAR szClass[kStrMax];
  2334. COPYDATASTRUCT cpds;
  2335. //
  2336. // Set up the copy data structure.
  2337. //
  2338. cpds.dwData = static_cast<DWORD>( TQueue::kQueueSignature );
  2339. cpds.cbData = _tcslen( pszPrinterName ) * sizeof(TCHAR) + sizeof(TCHAR);
  2340. cpds.lpData = reinterpret_cast<PVOID>( const_cast<LPTSTR>( pszPrinterName ) );
  2341. //
  2342. // Just another check to ensure the class name matches.
  2343. //
  2344. GetClassName(hwnd, szClass, COUNTOF(szClass));
  2345. if(!_tcsicmp( szClass, gszClassName ))
  2346. {
  2347. //
  2348. // Ask this queue window if you are viewing this printer.
  2349. //
  2350. if( SendMessage(hwnd, WM_COPYDATA, (WPARAM)hwndOwner, (LPARAM)&cpds) )
  2351. {
  2352. *phwnd = hwnd;
  2353. bStatus = TRUE;
  2354. break;
  2355. }
  2356. }
  2357. }
  2358. return bStatus;
  2359. }
  2360. VOID
  2361. TQueue::
  2362. vRemove(
  2363. IN LPCTSTR pszPrinterName
  2364. )
  2365. /*++
  2366. Routine Description:
  2367. Remove the printer queue view registry positon information.
  2368. Arguments:
  2369. Pointer to printer fully qualified printer name.
  2370. Return Value:
  2371. Nothing.
  2372. --*/
  2373. {
  2374. TPersist Persist( gszPrinterPositions, TPersist::kOpen|TPersist::kRead|TPersist::kWrite );
  2375. if( VALID_OBJ( Persist ) )
  2376. {
  2377. TStatusB bStatus;
  2378. bStatus DBGCHK = Persist.bRemove( pszPrinterName );
  2379. }
  2380. }
  2381. VOID
  2382. TQueue::
  2383. vCheckDefaultPrinterChanged(
  2384. VOID
  2385. )
  2386. /*++
  2387. Routine Description:
  2388. Update the print queue icon if needed.
  2389. Arguments:
  2390. None.
  2391. Return Value:
  2392. --*/
  2393. {
  2394. BOOL bDefaultPrinter;
  2395. TCHAR szPrinter[kPrinterBufMax];
  2396. _pPrinter->pszPrinterName(ARRAYSIZE(szPrinter), szPrinter);
  2397. bDefaultPrinter = CheckDefaultPrinter(szPrinter) == kDefault;
  2398. if( _bDefaultPrinter ^ bDefaultPrinter )
  2399. {
  2400. //
  2401. // If the default printer setting is changed and it related to
  2402. // current printer, change the window icon
  2403. //
  2404. _bDefaultPrinter = bDefaultPrinter;
  2405. CAutoHandleIcon shIconLarge, shIconSmall;
  2406. LoadPrinterIcons(szPrinter, &shIconLarge, &shIconSmall);
  2407. if( shIconLarge && shIconSmall )
  2408. {
  2409. _shIconLarge = shIconLarge.Detach();
  2410. _shIconSmall = shIconSmall.Detach();
  2411. SendMessage( _hwnd, WM_SETICON, ICON_BIG, (LPARAM)(HICON)_shIconLarge );
  2412. SendMessage( _hwnd, WM_SETICON, ICON_SMALL, (LPARAM)(HICON)_shIconSmall );
  2413. }
  2414. }
  2415. }