/**************/ /* tulostus.c */ /***************************************************************************** MODULE: tulostus.c PURPOSE: Aliohjelmakirjasto tulostamista varten. AUTHOR: Vesa Lappalainen 7.11.1992 USAGE: Projektiin vähintään oman ohjelman lisäksi tulostus.c, usein myös checker.c Jos tulostus halutaan voida keskeyttää, pitää tulostavassa aliohjelmassa tutkia globaalia muuttujaa GlobalPrinter.BreakPrinting ja lopettaa tulostus, mikäli tämä on päällä. Tämän tekee aliohjelma CheckMessage (0 = jatka, 1 = lopeta ). Aliohjelma on joko paketissa checker.c tai tulostus-kirjastossa, mikäli on määritelty vakio NO_CHECKER (Borland C++: Options,Compiler,Code Generation, Defines) Siis piirron pitkiin silmukoihin: if ( CheckMessage() ) ...lopeta_piirto...; Kannattaa laittaa ohjelmaan ehkä myös moniajon mahdollistava viestien käsittely (ks. checker.c), tällöin vakiota NO_CHECKER ei tarvita. ------------------------------------------------------------------------------ ============ Aliohjelmat: (tulostus.c, tulostus.h) ============ StartPrinting(HWND,int) - tulostuksen aloitus StopPrinting(HWND,HDC) - tulostuksen lopetus SetupPrinter(int) - kirjoittimen valinta InformPrinter(HINSTANCE) - ilmoitetaan esiintymän kahva CheckMessage() - halutaanko keskeyttää ============================= Aliohjelmien tarkempi kuvaus: ============================= ---------------------------------------- HDC StartPrinting(HWND hwnd,int MapMode) - tulostuksen aloitus ---------------------------------------- Palauttaa tulostimen laiteyhteyden kahvan, jos aloitus onnistuu. Tulostimeksi valitaan joko aikaisemmin SetupPrinter -kutsulla valittu tulostin tai Windowsin oletustulostin, mikäli em. kutsua ei ole käytetty. Tulostuksen aikana hwnd-ikkuna ei saa näppäin, eikä hiiriviestejä. int MapMode: (antaa MM_TEXT -tyylisen y kasvaa alaspäin) Määritellään millainen skaalaus tehdään tulostimen laiteyhteyteen. Yleensähän tulostimessa on eri kokoinen pikseli kuin näytöllä, joten näytölle suunniteltu kuva (näyttö esim 75 pt/inch) on varsin pieni tulostimella (laser esim. 300 pt/inch). Tällä parametrilla voidaan asia korjata. 0 = ei skaalausta >0 = paperin leveys vastaa näin montaa yksikköä PSC_DISPLAY = paperin leveys vastaa näytön leveyttä PSC_WINDOW = paperin leveys vastaa ikkunan leveyttä Riippumatta onko skaalaus määritelty tässä vai ei, voidaan omassa piirto-osuudessa ottaa käyttöön tietysti mikä tahansa muu skaalaus ( esim. SetMapMode(hdc,MM_LOMETRIC); antaisi 0.1 mm yksiköt ) Tämä paramteri on laiskaa käyttäjää varten, eli jos käyttäjä ei halua itse keksiä skaalausta (eikä ole alunperin käyttänytkään mitään skaalausta). ------------------------------------ int StopPrinting(HWND hwnd,HDC hpdc) - tulostuksen lopetus ------------------------------------ Tulostaa kirjoittimen hpdc sisään jääneen sivun ulos ja vapauttaa ikkunan hwnd. Sivua ei tulosteta ulos, mikäli tulostus on keskeytetty. ------------------------------------- int SetupPrinter(int OnlyPrinerSetup) - kirjoittimen valinta ------------------------------------- Valitsee kirjoittimen yms. ominaisuuksia. Paluttaa: 0 = valinta onnistui 1 = valinta ei onnistunut tai käyttäjä painoi ESC dialogissa int OnlyPrinterSetup: mitä menuja näyttöön 0 = myös PRINT-menu, jossa voidaan valita sivunumerot yms. tulos palautuu muuttujassa GlobalPrinter.pd (ks. PRINTDLG) 1 = vain Printer Setup Menu, jossa kirjoittimen valinta -------------------------------- void InformPrinter(HINSTANCE hInst) - ilmoitetaan esiintymän kahva. -------------------------------- Tarvitaan jos halutaan luoda keskeytysfunktio ja keskeytysdialogi eikä StartPrinting-kutsussa voida välittää ikkunan kahvaa. Tällöin projektiin tarvitaan myös tulostus.rc ja mielellään checker.c ja sen vaatimat muutokset ohjelmaan, jos tulostus saattaa kestää kauan. Voidaan myös käyttää tulostus-kirjaston CheckMessage-funktiota, ks. alla. ---------------------- int CheckMessage(void) - halutaanko keskeyttää ---------------------- Tämä funktio on määritelty joko kirjastossa checker.c tai kirjastossa tulostus.c sen mukaan on vakio NO_CHECKER esitelty vai ei. Palauttaa: 0 = ei haluta keskeyttää 1 = halutaan keskeyttää ===== Menu: (tulostus.rc) ===== PRINTMENU - käyttäjä voi korvata tämän omalla ======== Dialogi: (tulostus.rc) ======== BreakPrinter - jos käyttäjä kirjoittaa oman menun, pitää tämä dialogi löytyä jostakin ======== Viestit: (tulmenu.h) ======== IDM_FILE_PRINTER_SETUP - kun halutaan vaihtaa tulostin IDM_FILE_EXIT - kun halutaan lopettaa ohjelma IDM_FILE_PRINT - kun halutaan tulostaa - viestejä ei tarvita tulostuksessa, vaan käyttäjän omassa ohjelmassa. Siis nämä saa tietenkin määritellä uudelleenkin. Käyttäjän pitää lisätä ohjelmaansa em. viestien käsittely (yleensä aliohjelmilla PrinterSetup,StartPrinting-StopPrinting ja SendMessage() ) Tulostuksen aikana näyttöön tulee keskeytysdialogi, josta voidaan valita CANCEL, mikäli tulostus halutaan keskeyttää. Tulostuksen aikana Windows kutsuu myös alla määrättyä viestien katsomisohjelmaa, jotta muilla ohjelmilla olisi mahdollisuus pyöriä. ==================== Globaalit muuttujat: ==================== GlobalPrinter -tietue, josta ohjelmoija voi olla kiinnostunut ainakin tietueista GlobalPrinter.hPrinterDC - kirjoittimen DC GlobalPrinter.Printing - tulostus menossa GlobalPrinter.BreakPrinting - tulostus keskeytetty (pitää tutkia tätä muuttujaa) tai CheckMessage kyllä tekee saman GlobalPrinter.hInstance - ohjelman esiintymän kahva pitää sijoittaa tähän (ks. InformPrinter) GlobalPrinter.DevMode - Setupista valitut ominaisuudet (ks. DEVMODE) GlobalPrinter.pd - Maksimiarvot sivunumeroille jne. (ks. PRINTDLG ) ------------------------------------------------------------------------------ ============ Esimerkkejä: ============ Lyhin mahdollinen toimiva tulostus: projekti: HELLOPR2.C, tulostus.c tulostus.def -------------------------------------------------------------------------- #include #include "tulostus.h" int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HDC hdcPrinter; if ( (hdcPrinter = StartPrinting(NULL,0) ) != NULL) { TextOut(hdcPrinter,20,20,"Hello World!",12); StopPrinting(NULL,hdcPrinter); } return FALSE ; } -------------------------------------------------------------------------- Mukana myös kirjoittimen valinta ja tulostuksen keskeytys: projekti: HELLOPR3.C tulostus.c tulostus.rc KOLMIOM.C timer.c tulostus.def vakiot: NO_CHECKER (kolmiom.c kutsuu CheckMessage) -------------------------------------------------------------------------- #include #include "tulostus.h" #include "samplewm.h" int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HDC hdcPrinter; SetupPrinter(0); InformPrinter(hInstance); // Tarvitaan koska ei ikkunaa! if ( (hdcPrinter = StartPrinting(NULL,PSC_DISPLAY) ) != NULL) { MyDraw(NULL,hdcPrinter); StopPrinting(NULL,hdcPrinter); MessageBeep(MB_OK); } return FALSE ; } -------------------------------------------------------------------------- Normaalisti viestisilmukan WM_COMMAND -viestin alla oleva osa: projekti: MYMAIN.C tulostus.c tulostus.rc checker.c MYDRAW.C tulostus.def (mydraw.c kutsuu CheckMessage() ) -------------------------------------------------------------------------- ... case WM_COMMAND: switch ( GET_WM_COMMAND_ID(wParam,lParam) ) { case IDM_FILE_PRINT: if ( ( hDCPrinter = StartPrinting(hWnd,500) ) != NULL ) { MyDraw(hWnd,hDCPrinter); StopPrinting(hWnd,hDCPrinter); } return NULL; case IDM_FILE_PRINTER_SETUP: SetupPrinter(1); return NULL; case IDM_FILE_EXIT: SendMessage(hWnd,WM_DESTROY,0,0); return NULL; default: break; } ... -------------------------------------------------------------------------- *****************************************************************************/ /****************************************************************************/ /* Tulostuksen muuttujia ja aliohjelmia: */ /****************************************************************************/ #include #include #include #include "tulostus.h" #define CALLBACK_E CALLBACK _export PrinterType GlobalPrinter = {0,0}; /****************************************************************************/ #ifdef NO_CHECKER int CheckMessage(void) { if ( GlobalPrinter.Printing ) return GlobalPrinter.BreakPrinting; return 0; } #endif /****************************************************************************/ int SetupPrinter(int OnlySetupMenu) /* Kysytään tulostin standardi dialogilla. Palautetaan ** 0 = onnistui ** 1 = epäonnistui (tai painettiin ESC) ** ** Parametrilla OnlySetupMenu valitaan tuleeko vain Printer Setup Menu ** ( = 1, valitaan vain kirjoitin) vaiko täydellinen menu, jossa ** voidaan valita myös sivunumerot yms. (= 0). Tulostuksen rajat ** palautetaan globaalissa muuttujassa GlobalPrinter.pd (ks. PRINTDLG). ** ** Tässä vanhojen tietojen säilyttäminen dialogissa tuottaa aika paljon ** uutta vaivaa, koska PrintDlg -funktiota varten pitää allokoida ** globaalia muistia vanhojen arvojen palauttamiseksi. ** Ja kaikki vain siksi, ettei ohjelman lopuksi tarvitsisi kutsua ** globaalin muistin tyhjentävää aliohjelmaa. Nimittäin globaalit ** tilat voitaisiin aliohjelman lopuksi jättää hävittämättä ja käyttää ** seuraavalla kerralla samoja uudelleen, mutta lopuksi... ** Kyllä C++:an kanssa olisi helpompaa! */ { #define LSTRCPY(s1,s2) \ {_fstrncpy((LPSTR)s1,(LPCSTR)s2,sizeof(s1)); s1[sizeof(s1)-1]=0; } #define DEVSIZE ( (int) GlobalPrinter.DevSize ) #define NAMESIZE ( (int) GlobalPrinter.NameSize ) LPDEVNAMES lpDevNames; LPDEVMODE lpDevMode; if ( !GlobalPrinter.pd.lStructSize ) { /* Vain ekalla kerralla alustet. */ GlobalPrinter.pd.lStructSize = sizeof(PRINTDLG); GlobalPrinter.pd.hDevMode = NULL; GlobalPrinter.pd.hDevNames = NULL; } else { /* Muulloin otetaan käyttöön edellisen kerran valinnat */ GlobalPrinter.pd.hDevMode = GlobalAlloc(GMEM_MOVEABLE,DEVSIZE); GlobalPrinter.pd.hDevNames = GlobalAlloc(GMEM_MOVEABLE,NAMESIZE); } GlobalPrinter.pd.hwndOwner = NULL; GlobalPrinter.pd.hDC = 0; if ( OnlySetupMenu ) GlobalPrinter.pd.Flags |= PD_PRINTSETUP; else GlobalPrinter.pd.Flags &= !PD_PRINTSETUP; if ( GlobalPrinter.pd.hDevMode ) { /* Alustetaan vanhat tiedot */ lpDevMode = (LPDEVMODE) GlobalLock(GlobalPrinter.pd.hDevMode); _fmemcpy(lpDevMode,&GlobalPrinter.DevMode,DEVSIZE); GlobalUnlock(GlobalPrinter.pd.hDevMode); } if ( GlobalPrinter.pd.hDevNames ) { /* Alustetaan vanhat tiedot */ lpDevNames = (LPDEVNAMES) GlobalLock(GlobalPrinter.pd.hDevNames); _fmemcpy(lpDevNames,GlobalPrinter.DevNames,NAMESIZE); GlobalUnlock(GlobalPrinter.pd.hDevNames); } if ( !PrintDlg(&GlobalPrinter.pd) ) { /* Tuleeko dialogista virhettä? */ DWORD error = CommDlgExtendedError(); if ( GlobalPrinter.pd.hDevNames ) /* Muistin vapautus tietyisti! */ GlobalPrinter.pd.hDevNames = GlobalFree(GlobalPrinter.pd.hDevNames); if ( GlobalPrinter.pd.hDevMode ) /* Muistin vapautus tietyisti! */ GlobalPrinter.pd.hDevMode = GlobalFree(GlobalPrinter.pd.hDevMode); if ( error ) /* Seuraavalla kerralla oletus, jollei pain. ESC */ GlobalPrinter.pd.lStructSize = 0; return 1; } lpDevMode = (LPDEVMODE) GlobalLock(GlobalPrinter.pd.hDevMode); GlobalPrinter.DevSize = GlobalSize(GlobalPrinter.pd.hDevMode); if (DEVSIZE <= sizeof(GlobalPrinter.DevMode)+sizeof(GlobalPrinter.DevExtra)) _fmemcpy(&GlobalPrinter.DevMode,lpDevMode,DEVSIZE); /* Talteen */ GlobalUnlock(GlobalPrinter.pd.hDevMode); lpDevNames = (LPDEVNAMES) GlobalLock(GlobalPrinter.pd.hDevNames); GlobalPrinter.NameSize = GlobalSize(GlobalPrinter.pd.hDevNames); if ( NAMESIZE <= sizeof(GlobalPrinter.DevNames ) ) /* Talteen */ _fmemcpy(GlobalPrinter.DevNames,lpDevNames,NAMESIZE); LSTRCPY(GlobalPrinter.DriverName,lpDevNames + lpDevNames->wDriverOffset); LSTRCPY(GlobalPrinter.DeviceName,lpDevNames + lpDevNames->wDeviceOffset); LSTRCPY(GlobalPrinter.PortName ,lpDevNames + lpDevNames->wOutputOffset); GlobalUnlock(GlobalPrinter.pd.hDevNames); GlobalPrinter.pd.hDevNames = GlobalFree(GlobalPrinter.pd.hDevNames); GlobalPrinter.pd.hDevMode = GlobalFree(GlobalPrinter.pd.hDevMode); return 0; #undef NAMESIZE #undef DEVSIZE #undef LSTRCPY } /****************************************************************************/ static HDC GetPrinterDC(void) /* Palautetaan kahva tulostimen laiteyhteyteen, jos mahdollista. ** Tulostimeksi otetaan oletustulostin, mikäli mitään tulostinta ei ole ** asetettu, muuten käyttäjän valitseman tulostin. */ { char s[200],*Ptype,*Pdriver,*Pport; if ( !GlobalPrinter.DriverName[0] ) { GetProfileString("WINDOWS","DEVICE","",s,sizeof(s)); if ( (Ptype = strtok(s,",")) == NULL || (Pdriver = strtok(NULL,",")) == NULL || (Pport = strtok(NULL,",")) == NULL) return 0; } else { Pdriver = GlobalPrinter.DriverName; Ptype = GlobalPrinter.DeviceName; Pport = GlobalPrinter.PortName; } return CreateDC(Pdriver,Ptype,Pport,NULL); } /****************************************************************************/ int CALLBACK_E PrinterBreak(HDC hPrinterDC,int code) /* Viestisilmukka tulostuksen aikana. */ { #pragma argsused MSG msg; while ( !GlobalPrinter.BreakPrinting && PeekMessage(&msg,NULL,NULL,NULL,TRUE) ) { if ( IsDialogMessage(GlobalPrinter.PrinterDialogHWND,&msg ) ) continue; TranslateMessage(&msg); DispatchMessage(&msg); } return ( !GlobalPrinter.BreakPrinting ); } /****************************************************************************/ int CALLBACK_E PrinterDialogFunction(HWND hWnd,unsigned message, WPARAM wParam,LPARAM lParam) /* Tulostuksen keskeytysdialogin ikkunafunktio */ { #pragma argsused switch (message) { case WM_COMMAND: return ( GlobalPrinter.BreakPrinting = TRUE ); case WM_INITDIALOG: SetFocus(GetDlgItem(hWnd,IDCANCEL)); return TRUE; } return FALSE; } /****************************************************************************/ static void ScalePaper(HWND hwnd, HDC hPDC, int MapMode) /* Skaalataan MapModen mukaan paperin koko "sopivaksi" */ /* y-koordinaatti kasvaa alaspäin! */ { int scale; HDC hic; RECT rc; if ( !MapMode ) return; if ( MapMode > 0 ) scale = MapMode; else { switch ( MapMode ) { case PSC_DISPLAY: hic = CreateIC("DISPLAY",NULL,NULL,NULL); scale = GetDeviceCaps(hic,HORZRES); /* Näytön leveys */ DeleteDC(hic); break; case PSC_WINDOW: GetClientRect(hwnd,&rc); scale = rc.right; /* Ikkunan leveys */ break; default: return; } } SetMapMode(hPDC,MM_ISOTROPIC); SetWindowExtEx(hPDC,scale,scale,NULL); SetViewportExtEx(hPDC,GetDeviceCaps(hPDC,HORZRES),GetDeviceCaps(hPDC,VERTRES),NULL); } /****************************************************************************/ static int FreePrinterRes(void) /* Vapautetaan tulostuksen vaatimat resurssit. */ { if ( GlobalPrinter.PrinterDialogHWND ) DestroyWindow(GlobalPrinter.PrinterDialogHWND); if ( GlobalPrinter.PrinterBreakFunc ) (void)FreeProcInstance(GlobalPrinter.PrinterBreakFunc); if ( GlobalPrinter.PrinterDlgFunc ) (void)FreeProcInstance(GlobalPrinter.PrinterDlgFunc); if ( GlobalPrinter.hPrinterDC ) DeleteDC(GlobalPrinter.hPrinterDC); GlobalPrinter.PrinterDialogHWND = 0; GlobalPrinter.PrinterBreakFunc = 0; GlobalPrinter.PrinterDlgFunc = 0; GlobalPrinter.hPrinterDC = 0; GlobalPrinter.BreakPrinting = 0; GlobalPrinter.Printing = 0; return 0; } /****************************************************************************/ HDC StartPrinting(HWND hWnd,int MapMode) /* Tehdään tulostuksen aloittamiseksi tarvittavat toimenpiteet, eli ** globaalien muuttujien alustus, laiteyhteyden selvittäminen, ** uuden sivun aloittaminen ja keskeytysdialogin luominen. ** Palautetaan tulostimen laiteyhteyden kahva tai 0 jollei onnistu. ** MapMode >0 = paperin leveys skaalataan näin moneksiyksiköksi. ** Esim. ohjelma suunniteltu toimimaan 640x480 näytöllä, ** jolloin tämä voisi olla 640 ** 0 = ei skaalausta (käyttäjä voi skaalata itse ** PSC_DISPLAY = paperin leveys sama kuin ** näytön leveys ** PSC_WINDOW = paperin leveys sama kuin ** ikkunan leveys */ { if ( !GlobalPrinter.hInstance ) GlobalPrinter.hInstance = GetWindowInstance(hWnd); GlobalPrinter.BreakPrinting = 0; GlobalPrinter.Printing = 0; GlobalPrinter.hWnd = hWnd; if ( ( GlobalPrinter.hPrinterDC = GetPrinterDC() ) == 0 ) return 0; if ( MapMode ) ScalePaper(hWnd,GlobalPrinter.hPrinterDC,MapMode); GlobalPrinter.PrinterDlgFunc = MakeProcInstance((FARPROC)PrinterDialogFunction,GlobalPrinter.hInstance); GlobalPrinter.PrinterBreakFunc = MakeProcInstance((FARPROC)PrinterBreak,GlobalPrinter.hInstance); Escape(GlobalPrinter.hPrinterDC,SETABORTPROC,NULL, (LPSTR)(long)GlobalPrinter.PrinterBreakFunc,NULL); if ( Escape(GlobalPrinter.hPrinterDC,STARTDOC,8,"Tulostus",NULL) < 0 ) { MessageBox(hWnd,"Printer not responding!","",MB_OK | MB_ICONHAND); FreePrinterRes(); return 0; } GlobalPrinter.PrinterDialogHWND = CreateDialog(GlobalPrinter.hInstance,"BreakPrinter",hWnd, (DLGPROC)GlobalPrinter.PrinterDlgFunc); if (hWnd) EnableWindow(hWnd,FALSE); GlobalPrinter.Printing = 1; return GlobalPrinter.hPrinterDC; } /****************************************************************************/ int StopPrinting(HWND hWnd,HDC hPDC) /* Tulostuksen lopettamiseksi tarvittavat toimenpiteet, eli sivun ulosheitto, ** keskeytysdialogin hävittäminen ja globaalien muuttujien nollaus. */ { #pragma argsused if ( !GlobalPrinter.BreakPrinting ) { Escape(hPDC, NEWFRAME, 0 , 0L, 0L); Escape(hPDC, ENDDOC, 0 , 0L, 0L); } if (hWnd) EnableWindow(hWnd,TRUE); FreePrinterRes(); #ifndef NO_CHECKER CheckDelayMessages(hWnd); #endif return 1; } void InformPrinter(HINSTANCE hInstance) { GlobalPrinter.hInstance = hInstance; } /****************************************************************************/ /* Tulostuksen aliohjelmat loppuivat! */ /****************************************************************************/