Um zu verstehen, warum man in C einen Standard eingeführt hat bedarf es ein wenig mehr der Geschichte zu dieser Sprache.C C++ C/C++ Programmieren C Kurs Programmierung Systemprogrammierung Win32 api Win-Api Windows Systemprogrammierung C Programmierung Win32 api Win-Api Windows Systemprogrammierung Kapitel 9 Drucken:

Irgendwie fehlt dem Texteditor eine Funktion zum Ausdrucken des Textes. Dies geht einfacher als Sie vielleicht denken werden. Dazu benötigen Sie wieder die Headerdatei commctrl.h. Zuvor vielleicht nochmals die Ressource-Skriptedatei, die Headerdatei und der Quellcodes dieser Anwendung. Alle fett hervorgehobenen Stellen sind neu bzw. verändert gegenüber dem Listing im Kapitel zuvor.

9.1. Quellcodes zum Drucken            zurück  zum Inhaltsverzeichnis

Die Ressource-Skriptedatei

#include "resource.h"

/////////////////////////////////////////////////////////////////
//
// Menü
//

IDR_MENU MENU DISCARDABLE
BEGIN
    POPUP "Datei"
    BEGIN
        MENUITEM "Neu",                          ID_FILE_NEW
        MENUITEM "Öffnen...",                    ID_FILE_OPEN
        MENUITEM "Speichern unter...",           ID_FILE_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "Drucken",                      ID_PRINT
        MENUITEM SEPARATOR
        MENUITEM "Beenden",                      ID_FILE_EXIT
    END
    POPUP "Eigenschaften"
    BEGIN
        MENUITEM "Schriftart ändern",            ID_FORMAT_FONT
        MENUITEM "Schriftfarbe ändern",          ID_FONT_COLOR
        MENUITEM "Hintergrundfarbe ändern",      ID_BK_COLOR
    END
    POPUP "Über ..."
    BEGIN
        MENUITEM "Version",                      ID_VERSION
    END
END

/////////////////////////////////////////////////////////////////
//
//Icon
//
ID_ICON              ICON        "Edit.ico"

Die Headerdatei resource.h

#define IDR_MENU                   100

#define ID_FILE_EXIT                    40001
#define ID_FILE_OPEN                    40002
#define ID_FILE_SAVEAS                  40003
#define ID_FILE_NEW                     40004
#define ID_FORMAT_FONT                  40005
#define ID_FONT_COLOR                   40006
#define ID_BK_COLOR                     40007
#define ID_VERSION                      40008
#define ID_ICON                         40009
#define ID_PRINT                        40010

#define IDC_MAIN_EDIT                   101
#define IDC_MAIN_TOOL                   102
#define IDC_MAIN_STATUS                 103

Der komplette Quellcode

#include <windows.h>
#include <commctrl.h>
#include "resource.h"

#define WEISS   RGB(255,255,255)
#define SCHWARZ RGB(0,0,0)

const char g_szClassName[] = "Pronix - Texteditor";

static LOGFONT logfont ;
static HFONT hFont ;

HWND hEdit;
HWND hwnd;

COLORREF backcolor    = WEISS,
         textcolor    = SCHWARZ,
         currentcolor = SCHWARZ;
CHOOSECOLOR choosecolor;
HBRUSH hbrush;
static COLORREF customcolors[16];
static LOGBRUSH logbrush;

static char szFileName[MAX_PATH] = "";
HINSTANCE hInst;

/* Farben für das Edit-Fenster initialisieren */
void SetupChooseColor(CHOOSECOLOR *cc,COLORREF *customclrs,
                      HWND hwnd, COLORREF init)
{
   int ca;
   for(ca = 0; ca < 16; ca++)
      *(customclrs+ca) = SCHWARZ;

   cc->lStructSize = sizeof(CHOOSECOLOR);
   cc->hwndOwner = hwnd;
   cc->rgbResult = init;
   cc->lpCustColors = customclrs;
   cc->Flags = CC_ANYCOLOR|/*CC_FULLOPEN|*/CC_RGBINIT;
}

/* Schriftarten (Fonts) auswählen */
BOOL FontChooseFont (HWND hwnd)
{
     CHOOSEFONT cf ;

     ZeroMemory(&cf, sizeof(cf));

     cf.lStructSize    = sizeof (CHOOSEFONT) ;
     cf.hwndOwner      = hwnd ;
     cf.lpLogFont      = &logfont ;
     cf.Flags          = CF_INITTOLOGFONTSTRUCT |
                         CF_SCREENFONTS | CF_EFFECTS ;
     //Dialogbox:Schriftart auswählen
     return ChooseFont (&cf) ;
}

/* Fonts initialisieren */
void FontInitialize (HWND hwndEdit)
{
     GetObject (GetStockObject (SYSTEM_FONT), sizeof (LOGFONT),
                (PTSTR) &logfont) ;

     hFont = CreateFontIndirect (&logfont) ;
     SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFont, 0) ;
}

/* Neue ausgeählte Schriftart verwenden */
void FontSetFont (HWND hwndEdit)
{
     HFONT hFontNew ;
     RECT  rect ;

     hFontNew = CreateFontIndirect (&logfont) ;
     SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFontNew, 0) ;
     DeleteObject (hFont) ;
     hFont = hFontNew ;
     GetClientRect (hwndEdit, &rect) ;
     InvalidateRect (hwndEdit, &rect, TRUE) ;
}


BOOL PrintFile (HINSTANCE hInst, HWND hwnd, HWND hwndEdit,
                       PTSTR szTitleName)
{
     DOCINFO  di;
     PRINTDLG pd ;
     BOOL bSuccess ;
     int yChar, iCharsPerLine, iLinesPerPage, iTotalLines,
         iTotalPages, iPage, iLine, iLineNum ;
     PTSTR pstrBuffer ;
     TCHAR szJobName [64 + MAX_PATH] ;
     TEXTMETRIC tm ;
     WORD iColCopy, iNoiColCopy ;

          // Invoke Print common dialog box
     ZeroMemory(&pd, sizeof(pd));
     pd.lStructSize         = sizeof (PRINTDLG) ;
     pd.hwndOwner           = hwnd ;
     pd.hDevMode            = NULL ;
     pd.hDevNames           = NULL ;
     pd.Flags               = PD_ALLPAGES | PD_COLLATE |
                              PD_RETURNDC | PD_NOSELECTION |
                              PD_PRINTSETUP ;
     pd.nCopies             = 1 ;

     PrintDlg (&pd);

     iTotalLines = SendMessage (hwndEdit, EM_GETLINECOUNT, 0, 0);
     if( iTotalLines == 0 )
        return TRUE;

          // Calculate necessary metrics for file

     GetTextMetrics (pd.hDC, &tm) ;
     yChar = tm.tmHeight + tm.tmExternalLeading ;

     iCharsPerLine = GetDeviceCaps (pd.hDC, HORZRES) / tm.tmAveCharWidth ;
     iLinesPerPage = GetDeviceCaps (pd.hDC, VERTRES) / yChar ;
     iTotalPages   = (iTotalLines + iLinesPerPage - 1) / iLinesPerPage ;

          // Allocate a buffer for each line of text

     pstrBuffer =(PTSTR) malloc (sizeof (TCHAR) * (iCharsPerLine + 1)) ;

          // Display the printing dialog box

     bSuccess   = TRUE ;

     SendDlgItemMessage( hwnd, IDC_MAIN_STATUS,
                      SB_SETTEXT, 0, (LPARAM)"Wird gedruckt...");
     SendDlgItemMessage( hwnd, IDC_MAIN_STATUS,
                         SB_SETTEXT, 1, (LPARAM)szFileName);

          // Start the document
     di.cbSize = sizeof(DOCINFO);
     di.lpszDocName = szFileName ;

     if (StartDoc (pd.hDC, &di) > 0)
     {
               // Collation requires this loop and iNoiColCopy

       for (iColCopy = 0 ;
          iColCopy<((WORD) pd.Flags & PD_COLLATE ? pd.nCopies:1);
          iColCopy++)
             {
                for (iPage = 0 ; iPage < iTotalPages ; iPage++)
                  {
                     for (iNoiColCopy = 0 ;
                          iNoiColCopy<( pd.Flags&PD_COLLATE?1
                                        :pd.nCopies );
                          iNoiColCopy++)
                        {
                           if (StartPage (pd.hDC) < 0)
                              {
                                 bSuccess = FALSE ;
                                 break ;
                              }
                        // For each page, print the lines
                        for (iLine=0;iLine<iLinesPerPage;iLine++)
                           {
                             iLineNum=iLinesPerPage*iPage+iLine;

                              if (iLineNum > iTotalLines)
                                   break ;

                            *(int *) pstrBuffer = iCharsPerLine ;
                            TextOut (pd.hDC, 0, yChar * iLine,
                                     pstrBuffer,(int) SendMessage
                                     (hwndEdit, EM_GETLINE,
                                     (WPARAM) iLineNum, (LPARAM)
                                     pstrBuffer) );
                           }

                         if (EndPage (pd.hDC) < 0)
                         {
                              bSuccess = FALSE ;
                              break ;
                         }

                    }

                    if (!bSuccess)
                         break ;
               }

               if (!bSuccess)
                    break ;
          }
     }
     else
          bSuccess = FALSE ;

     if (bSuccess)
          EndDoc (pd.hDC) ;

     free (pstrBuffer) ;
     DeleteDC (pd.hDC) ;
     return bSuccess;
}


/* DATEI/ÖFFNEN... Text in das Edit-Feld laden */
BOOL LoadText(HWND hEdit, LPCTSTR pszFileName)
{
   HANDLE hFile;
   BOOL bSuccess = FALSE;

   hFile = CreateFile(pszFileName, GENERIC_READ,
                       FILE_SHARE_READ, NULL,
                       OPEN_EXISTING, 0, NULL);
   if(hFile != INVALID_HANDLE_VALUE)
      {
         DWORD dwFileSize;

         dwFileSize = GetFileSize(hFile, NULL);
         if(dwFileSize != 0xFFFFFFFF)
         {
            LPSTR pszFileText;

           pszFileText=(char *)GlobalAlloc(GPTR, dwFileSize + 1);
            if(pszFileText != NULL)
            {
               DWORD dwRead;
               if(ReadFile( hFile, pszFileText, dwFileSize,
                             &dwRead, NULL )   )
                  {
                     pszFileText[dwFileSize] = '\0';
                     if(SetWindowText(hEdit, pszFileText))
                           bSuccess = TRUE;
                  }
               GlobalFree(pszFileText);
            }
         }
         CloseHandle(hFile);
      }
   return bSuccess;
}

/* DATEI/SPEICHERN UNTER ... Denn Text abspeichern */
BOOL SaveText(HWND hEdit, LPCTSTR pszFileName)
{
   HANDLE hFile;
   BOOL bSuccess = FALSE;

   hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL,
                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   if(hFile != INVALID_HANDLE_VALUE)
      {
         DWORD dwTextLength;

         dwTextLength = GetWindowTextLength(hEdit);
         if(dwTextLength > 0)
            {
               LPSTR pszText;
               DWORD dwBufferSize = dwTextLength + 1;
               pszText =(char *)GlobalAlloc(GPTR, dwBufferSize);
               if(pszText != NULL)
                 {
                  if(GetWindowText(hEdit, pszText, dwBufferSize))
                     {
                         DWORD dwWritten;
                         if(WriteFile(hFile,pszText,dwTextLength,
                             &dwWritten, NULL )   )
                                    bSuccess = TRUE;
                     }
                  GlobalFree(pszText);
                 }
            }
         CloseHandle(hFile);
      }
   return bSuccess;
}

/* Dialogbox Datei öffnen */
void OpenFileBox(HWND hwnd)
{
   OPENFILENAME ofn;
   //char szFileName[MAX_PATH] = "";

   ZeroMemory(&ofn, sizeof(ofn));

   ofn.lStructSize = sizeof(ofn);
   ofn.hwndOwner = hwnd;
   ofn.lpstrFilter = "Text Datei (*.txt)\0*.txt\0"
                     "C Datei (*.c)\0*.c\0"
                     "Alle Dateien (*.*)\0*.*\0";
   ofn.lpstrFile = szFileName;
   ofn.nMaxFile = MAX_PATH;
   ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST |
               OFN_HIDEREADONLY;
   ofn.lpstrDefExt = "txt";

   if(GetOpenFileName(&ofn))
      {
         HWND hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
         if(LoadText(hEdit, szFileName))
            {
               SendDlgItemMessage(hwnd, IDC_MAIN_STATUS,
                           SB_SETTEXT, 0, (LPARAM)"Geöffnet...");
               SendDlgItemMessage(hwnd, IDC_MAIN_STATUS,
                              SB_SETTEXT, 1, (LPARAM)szFileName);
            }
      }
}

/* Dialogbox: Datei speichern */
void SaveFileBox(HWND hwnd)
{
   OPENFILENAME ofn;
   //char szFileName[MAX_PATH] = "";

   ZeroMemory(&ofn, sizeof(ofn));

   ofn.lStructSize = sizeof(ofn);
   ofn.hwndOwner = hwnd;
   ofn.lpstrFilter = "Text Datei (*.txt)\0*.txt\0"
                     "C Datei (*.c)\0*.c\0"
                     "Alle Dateien (*.*)\0*.*\0";
   ofn.lpstrFile = szFileName;
   ofn.nMaxFile = MAX_PATH;
   ofn.lpstrDefExt = "txt";
   ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST |
               OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;

   if(GetSaveFileName(&ofn))
      {
         HWND hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
         if(SaveText(hEdit, szFileName))
            {
               SendDlgItemMessage(hwnd, IDC_MAIN_STATUS,
                       SB_SETTEXT, 0, (LPARAM)"Gespeichert...");
               SendDlgItemMessage(hwnd, IDC_MAIN_STATUS,
                        SB_SETTEXT, 1, (LPARAM)szFileName);
            }
      }
}

/* Die WndProc() Prozedur */
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam,
                                              LPARAM lParam)
{
   switch(msg)
      {
         case WM_CREATE:
            {
               HWND hTool;
               TBBUTTON tbb[4];
               TBADDBITMAP tbab;

               HWND hStatus;

               int statwidths[] = {100, -1};
               //Textfeld erstellen
               hEdit=CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
                                    WS_CHILD | WS_VISIBLE |
                                    WS_VSCROLL | WS_HSCROLL |
                                    ES_MULTILINE|ES_AUTOVSCROLL|
                                    ES_AUTOHSCROLL,
                                    0, 0, 100, 100, hwnd,
                                    (HMENU)IDC_MAIN_EDIT,
                                    GetModuleHandle(NULL), NULL);
               if(hEdit == NULL)
                  MessageBox(hwnd,
                       "Fehler beim Erstellen des Textfelds",
                       "Fehler", MB_OK | MB_ICONERROR);

               FontInitialize (hEdit);

               logbrush.lbHatch = 0;
               logbrush.lbStyle = BS_SOLID;
               logbrush.lbColor = WEISS;

               SetupChooseColor( &choosecolor,customcolors,
                                 hEdit,currentcolor);

               //Werkzeugleiste erstellen
               hTool = CreateWindowEx(0,TOOLBARCLASSNAME, NULL,
                                      WS_CHILD|WS_VISIBLE,
                                      0, 0, 0, 0,hwnd,
                                      (HMENU)IDC_MAIN_TOOL,
                                      GetModuleHandle(NULL),
                                      NULL);
               if(hTool == NULL)
                  MessageBox(hwnd,
                         "Kann keine Werkzeugleiste erstellen",
                         "Fehler", MB_OK | MB_ICONERROR);

               SendMessage( hTool, TB_BUTTONSTRUCTSIZE,
                            (WPARAM)sizeof(TBBUTTON), 0 );

               tbab.hInst = HINST_COMMCTRL;
               tbab.nID = IDB_STD_LARGE_COLOR;
               SendMessage(hTool,TB_ADDBITMAP,0,(LPARAM)&tbab);

               ZeroMemory(tbb, sizeof(tbb));
               tbb[0].iBitmap = STD_FILENEW;
               tbb[0].fsState = TBSTATE_ENABLED;
               tbb[0].fsStyle = TBSTYLE_BUTTON;
               tbb[0].idCommand = ID_FILE_NEW;
               //tbb[0].iString = (int)"Neu";

               tbb[1].iBitmap = STD_FILEOPEN;
               tbb[1].fsState = TBSTATE_ENABLED;
               tbb[1].fsStyle = TBSTYLE_BUTTON;
               tbb[1].idCommand = ID_FILE_OPEN;
               //tbb[1].iString = (int)"Öffnen";

               tbb[2].iBitmap = STD_FILESAVE;
               tbb[2].fsState = TBSTATE_ENABLED;
               tbb[2].fsStyle = TBSTYLE_BUTTON;
               tbb[2].idCommand = ID_FILE_SAVEAS;
               //tbb[2].iString = (int)"Speichern unter...";

               tbb[3].iBitmap = STD_PRINT;
               tbb[3].fsState = TBSTATE_ENABLED;
               tbb[3].fsStyle = TBSTYLE_BUTTON;
               tbb[3].idCommand = ID_PRINT;

               SendMessage( hTool, TB_ADDBUTTONS,
                            sizeof(tbb)/sizeof(TBBUTTON),
                            (LPARAM)&tbb );

               //Statusleiste erstellen
               hStatus = CreateWindowEx(0,STATUSCLASSNAME,NULL,
                                        WS_CHILD | WS_VISIBLE |
                                        SBARS_SIZEGRIP,
                                        0, 0, 0, 0, hwnd,
                                        (HMENU)IDC_MAIN_STATUS,
                                        GetModuleHandle(NULL),
                                        NULL);

               SendMessage( hStatus, SB_SETPARTS,
                            sizeof(statwidths)/sizeof(int),
                            (LPARAM)statwidths );
               SendMessage( hStatus, SB_SETTEXT, 0,
                            (LPARAM)"Bereit..." );
            }//Ende WM_CREATE
         break;
         case WM_SIZE:
            {
               HWND hTool;
               RECT rcTool;
               int iToolHeight;

               HWND hStatus;
               RECT rcStatus;
               int iStatusHeight;

               int iEditHeight;
               RECT rcClient;

               hTool = GetDlgItem(hwnd, IDC_MAIN_TOOL);
               SendMessage(hTool, TB_AUTOSIZE, 0, 0);

               GetWindowRect(hTool, &rcTool);
               iToolHeight = rcTool.bottom - rcTool.top;

               hStatus = GetDlgItem(hwnd, IDC_MAIN_STATUS);
               SendMessage(hStatus, WM_SIZE, 0, 0);

               GetWindowRect(hStatus, &rcStatus);
               iStatusHeight = rcStatus.bottom - rcStatus.top;

               GetClientRect(hwnd, &rcClient);

               iEditHeight = rcClient.bottom - iToolHeight -
                             iStatusHeight;

               hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
               SetWindowPos( hEdit, NULL, 0, iToolHeight,
                             rcClient.right, iEditHeight,
                             SWP_NOZORDER );
            }//Ende WM_SIZE
                break;
        case WM_CTLCOLOREDIT:
            {
               SetTextColor((HDC)wParam,textcolor);
               SetBkMode((HDC)wParam,OPAQUE);
               SetBkColor((HDC)wParam,backcolor);
               logbrush.lbColor = backcolor;
               DeleteObject(hbrush);
               hbrush = CreateBrushIndirect(&logbrush);
               return (long)hbrush;
            }// Ende WM_CTLCOLOREDIT
         break;
         case WM_CLOSE:
            DestroyWindow(hwnd);
         break;
         case WM_DESTROY:
            PostQuitMessage(0);
         break;
         case WM_COMMAND:
            switch(LOWORD(wParam))
               {
                  case ID_FILE_EXIT:
                     PostMessage(hwnd, WM_CLOSE, 0, 0);
                  break;
                  case ID_FILE_NEW:
                     SetDlgItemText(hwnd, IDC_MAIN_EDIT, "");
                     SendDlgItemMessage(hwnd, IDC_MAIN_STATUS,
                         SB_SETTEXT, 0, (LPARAM)"Neue Datei...");
                     SendDlgItemMessage(hwnd, IDC_MAIN_STATUS,
                           SB_SETTEXT, 1, (LPARAM)"Unbenannt");
                  break;
                  case ID_FILE_OPEN:
                     OpenFileBox(hwnd);
                  break;
                  case ID_FILE_SAVEAS:
                     SaveFileBox(hwnd);
                  break;
                  case ID_FORMAT_FONT:
                     {
                        FontChooseFont (hEdit);
                        FontSetFont (hEdit);
                     }
                   break;
                   case ID_FONT_COLOR:
                   if(ChooseColor(&choosecolor))
                      {
                         currentcolor = choosecolor.rgbResult;
                         textcolor = currentcolor;
                         InvalidateRect(hEdit, 0, TRUE);
                      }
                   break;
                   case ID_BK_COLOR:
                   if(ChooseColor(&choosecolor))
                      {
                         currentcolor = choosecolor.rgbResult;
                         backcolor = currentcolor;
                         InvalidateRect(hEdit, 0, TRUE);
                      }
                   break;
                   case ID_PRINT:
                       //Drucken
                   PrintFile (hInst, hwnd, hEdit, szFileName);
                   break;
                   case ID_VERSION:
                       MessageBox( NULL,
                       "Ein einfacher Texteditor\nVersion 0.1",
                       "Version",MB_OK );
                   break;
               }//switch
         break;//WM_COMMAND
         default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
      }
   return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
   WNDCLASSEX wc;
   MSG Msg;

   //InitCommonControls();

   wc.cbSize = sizeof(WNDCLASSEX);
   wc.style = 0;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance         = hInstance;
   wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(ID_ICON));
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
   wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
   wc.lpszClassName = g_szClassName;
   wc.hIconSm = (HICON)LoadImage( GetModuleHandle(NULL),
                                  MAKEINTRESOURCE(ID_ICON),
                                  IMAGE_ICON, 16, 16, 0 );

   if(!RegisterClassEx(&wc))
      {
         MessageBox( NULL,
              "Fensterklasse konnte nicht Initialisiert werden!",
              "Error!",
              MB_ICONEXCLAMATION | MB_OK);
         return 0;
      }

   hwnd = CreateWindowEx(0, g_szClassName,"Pronix - Texteditor",
                         WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                         CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
                         NULL, NULL, hInstance, NULL);

   if(hwnd == NULL)
      {
          MessageBox( NULL, "Konnte kein Fenster erstellen!",
                      "Error!",MB_ICONEXCLAMATION | MB_OK  );
          return 0;
      }

   hInst = hInstance;
   ShowWindow(hwnd, nCmdShow);
   UpdateWindow(hwnd);

   while(GetMessage(&Msg, NULL, 0, 0) > 0)
      {
         TranslateMessage(&Msg);
         DispatchMessage(&Msg);
      }
   return Msg.wParam;
}

9.2. Analyse des Quellcodes            zurück  zum Inhaltsverzeichnis

Beim Betrachten des Listings dürfte Ihnen aufgefallen sein, dass sich nicht sehr viel verändert hat. In der Werkzeugleiste wurde noch ein Button mehr zum Drucken hinzugefügt, dass sich somit folgendes Bild des Texteditors ergibt:

Texteditor mit Druck-Funktion

Texteditor mit Druck-Funktion

Wenn also die Schaltfläche oder der Menüpunkt zum Drucken ausgewählt wurde, wird unsere selbst geschriebene Funktion PrintFile() aufgerufen.

BOOL PrintFile (HINSTANCE hInst, HWND hwnd, HWND hwndEdit,
                       PTSTR szTitleName)
{
     DOCINFO  di;
     PRINTDLG pd ;
     BOOL bSuccess ;
     int yChar, iCharsPerLine, iLinesPerPage, iTotalLines,
         iTotalPages, iPage, iLine, iLineNum ;
     PTSTR pstrBuffer ;
     TCHAR szJobName [64 + MAX_PATH] ;
     TEXTMETRIC tm ;
     WORD iColCopy, iNoiColCopy ;

     ZeroMemory(&pd, sizeof(pd));
     pd.lStructSize         = sizeof (PRINTDLG) ;
     pd.hwndOwner           = hwnd ;
     pd.hDevMode            = NULL ;
     pd.hDevNames           = NULL ;
     pd.Flags               = PD_ALLPAGES | PD_COLLATE |
                              PD_RETURNDC | PD_NOSELECTION |
                              PD_PRINTSETUP ;
     pd.nCopies             = 1 ;

     PrintDlg (&pd);

     iTotalLines = SendMessage (hwndEdit, EM_GETLINECOUNT, 0, 0);
     if( iTotalLines == 0 )
        return TRUE;

          // Calculate necessary metrics for file

     GetTextMetrics (pd.hDC, &tm) ;
     yChar = tm.tmHeight + tm.tmExternalLeading ;

     iCharsPerLine = GetDeviceCaps (pd.hDC, HORZRES) / tm.tmAveCharWidth ;
     iLinesPerPage = GetDeviceCaps (pd.hDC, VERTRES) / yChar ;
     iTotalPages   = (iTotalLines + iLinesPerPage - 1) / iLinesPerPage ;


     pstrBuffer =(PTSTR) malloc
                   (sizeof (TCHAR) * (iCharsPerLine + 1)) ;

     bSuccess   = TRUE ;

     SendDlgItemMessage( hwnd, IDC_MAIN_STATUS,
                      SB_SETTEXT, 0, (LPARAM)"Wird gedruckt...");
     SendDlgItemMessage( hwnd, IDC_MAIN_STATUS,
                         SB_SETTEXT, 1, (LPARAM)szFileName);

     di.cbSize = sizeof(DOCINFO);
     di.lpszDocName = szFileName ;

     if (StartDoc (pd.hDC, &di) > 0)
     {
       for (iColCopy = 0 ;
          iColCopy<((WORD) pd.Flags & PD_COLLATE ? pd.nCopies:1);
          iColCopy++)
             {
                for (iPage = 0 ; iPage < iTotalPages ; iPage++)
                  {
                     for (iNoiColCopy = 0 ;
                          iNoiColCopy<( pd.Flags&PD_COLLATE?1
                                        :pd.nCopies );
                          iNoiColCopy++)
                        {
                           if (StartPage (pd.hDC) < 0)
                              {
                                 bSuccess = FALSE ;
                                 break ;
                              }
                        for (iLine=0;iLine<iLinesPerPage;iLine++)
                           {
                             iLineNum=iLinesPerPage*iPage+iLine;

                              if (iLineNum > iTotalLines)
                                   break ;

                            *(int *) pstrBuffer = iCharsPerLine ;
                            TextOut (pd.hDC, 0, yChar * iLine,
                                     pstrBuffer,(int) SendMessage
                                     (hwndEdit, EM_GETLINE,
                                     (WPARAM) iLineNum, (LPARAM)
                                     pstrBuffer) );
                           }

                         if (EndPage (pd.hDC) < 0)
                         {
                              bSuccess = FALSE ;
                              break ;
                         }

                    }

                    if (!bSuccess)
                         break ;
               }

               if (!bSuccess)
                    break ;
          }
     }
     else
          bSuccess = FALSE ;

     if (bSuccess)
          EndDoc (pd.hDC) ;

     free (pstrBuffer) ;
     DeleteDC (pd.hDC) ;
     return bSuccess;
}

Da das Programm nicht einfach einen Text ausdrucken soll, sondern auch dem Anwender beim Drucken über die windowstypische Dialogbox Einstellungen machen lassen soll, deklarieren Sie die Struktur PRINTDLG printer. Damit diese Dialog-Box eingesetzt werden kann, wird die Headerdatei commctrl.h benötigt. Diese Dialog-Box wird mit der Funktion PrintDlg() aufgerufen.

Druckereinrichtung

Druckereinrichtung

Um also eine Dialog-Box zur Druckereinrichtung zu erzeugen, müssen Sie zuvor die Struktur PRINTDLG mit einigen Werten initialisieren:

     ZeroMemory(&pd, sizeof(pd));
     pd.lStructSize         = sizeof (PRINTDLG) ;
     pd.hwndOwner           = hwnd ;
     pd.hDevMode            = NULL ;
     pd.hDevNames           = NULL ;
     pd.Flags               = PD_ALLPAGES | PD_COLLATE |
                              PD_RETURNDC | PD_NOSELECTION |
                              PD_PRINTSETUP ;
     pd.nCopies             = 1 ;

Der Struktur PRINTDLG beinhaltet eine Menge Strukturvariablen. Wir initialisieren hierbei nur die Nötigsten. Die Restlichen werden wieder mit ZeroMemory() mit 0 vorbelegt. lStructSize bekommt die Größe der Struktur in Bytes hwndOwner ist für den Fenster-Handle, wofür die Druckereinrichtung verwendet werden soll. Die Strukturelemente hDevMode und hDevNames initialisieren Sie mit NULL, womit veranlasst wird, das sich die Dialogfunktion selbst um den Speicherplatz kümmert, um darin die Strukturen DEVMODE und DEVNAMES anzulegen, welche die Druckerfähigkeit und den (die) installierten Drucker beinhalten. Mit den Flags können Sie eine von vielen Angaben machen, womit Sie Ihre Dialog-Box für den Drucker initialisieren können. Da Sie zum Drucken etwas später einen Gerätekontex (Device Context) benötigen, wurde hier das Flag PD_RETURNDC verwendet. Mit PD_PRINTSETUP geben Sie an, das dass System die "Druckereinrichtung"-Dialogbox anzeigt und nicht die Drucker-Dialog-Box, wie Sie in folgender Abbildung sehen:

Allgemeine Drucker-Dialog-Box

Allgemeine Drucker-Dialog-Box

Es gibt in der Tat noch eine ganze Menge mehr Flags und auch Strukturvariablen in PRINTDLG. Für mehr und genauere Angaben sei wieder die MSDN-Dokumentation empfohlen.

Jetzt wird mit der Funktion PrintDlg() die Dialog-Box zur Druckereinrichtung erzeugt und angezeigt. Nun zu einer einfachen aber sehr nützlichen Nachricht für unsere mehrzeiliges Editfeld:

iTotalLines = SendMessage (hwndEdit, EM_GETLINECOUNT, 0, 0); 

Damit wird veranlasst, dass Sie die Anzahl der Zeilen, die sich in Ihrem mehrzeiligen Editfeld befinden, erhalten. Anschließend ermitteln Sie mit der Funktion GetTextMetrics() Daten der aktuellen Schrift, indem die Struktur TEXTMETRIC mit entsprechenden Informationen gefüllt wird. Die Funktion benötigt als ersten Parameter einen Gerätekontex und als zweiten einen Zeiger auf die TEXTMETRICS-Struktur.

Zwei Werte dieser Struktur verwenden Sie gleich in der nächsten Zeile:

yChar = tm.tmHeight + tm.tmExternalLeading ;

Damit bekommt die Variable yChar die Höhe eines Zeichens. Diesen Wert errechnen Sie hier mit der Höhe des Zeichens (tmHeight) plus den zusätzlichen Abstand, den jede Zeile dazwischen einfügt (tmExternalLeading).

In den nächsten beiden Zeilen verwenden Sie zum ersten Mal die Funktion: GetDeviceCaps():

iCharsPerLine=GetDeviceCaps(pd.hDC, HORZRES)/tm.tmAveCharWidth ;
iLinesPerPage = GetDeviceCaps (pd.hDC, VERTRES) / yChar ;

Mit dieser Funktion lassen sich gerätspezifische Informationen wie die Auflösung des Bildschirms usw. herausfinden. Mit dem ersten Parameter geben Sie den Gerätekontex an und mit dem Zweiten eine Konstante, was für Informationen Sie gerne hätten. Alleine in einem Buch, dass hier vor mir liegt, sind fünf Seiten solcher Konstanten aufgelistet. Mit HORZRES können Sie die Breite des Bildschirms in Pixel ermitteln und mit VERTRES eben die Höhe in Rasterzeilen.

In der ersten Zeile errechnen Sie die Anzahl Zeichen pro Zeile. Diese bekommen Sie, in dem Sie die Breite des Bildschirms durch die durchschnittliche Zeichenbreite der Schrift (tmAveCharWidth) teilen. In der zweiten Zeile machen Sie das Gleiche mit der Anzahl Zeilen pro Seite.

Anschließend Allozieren Sie für jede Textzeile einen Speicher vom Heap.

Die Statusleiste verändern Sie ebenfalls noch, mit der Information, dass gedruckt wird (SendDlgItemMessage).

     ZeroMemory(&di, sizeof(DOCINFO));
     di.cbSize = sizeof(DOCINFO);
     di.lpszDocName = szFileName ;

Jetzt müssen Sie die Struktur DOCINFO und seine Variablen initialisieren. Diese Struktur beinhaltet Ein/Ausgabe-Dateinamen und weitere Informationen, die für die Funktion StartDoc() nötig sind. Bis auf die Strukturvariablen cbSize und lpszDocName belegen Sie hier alle anderen Variablen durch ZeroMemory() mit 0. cbSize benötigt die Größe der Struktur und lpszDocName den Namen der Datei, die gedruckt werden soll. Diesen Namen haben Sie in diesem Listing global angelegt und erhalten diesen durch die Dialog-Box zum Öffnen bzw. Speichern der Datei (siehe Kapitel zuvor). Diesen Namen können Sie dann in der Druckerwarteschlange identifizieren. Würde hierfür bspw. der String "Tesdruck" verwenden, stünde Folgendes in der Druckauftrag-Schlange:

Drucker-Warteschlange

Drucker-Warteschlange

In der nächsten Zeile starten Sie mit der Funktion StartDoc() einen Druckerauftrag an den vorgegebenen Drucker mit dem Gerätekontext pd.hDC und einen Zeiger auf die DOCINFO-Struktur, welche den Namen der Ausgabedatei und der Dokumentendatei enthält.

Jetzt folgen drei verschachtelte for-Schleifen. Die erste dient zum durchlaufen von Anzahl der Kopien, die Sie drucken wollen (iColCopy). Die zweite for-Schleife durchläuft die Anzahl der Seiten die gedruckt werden sollen (iPage). Die dritte for-Schleife dient wiederum dazu, wie viele Kopien von jeder Seite erstellt werden sollen (iNoiColCopy). Die Anzahl der Kopien überprüfen Sie, in dem Sie testen, ob Sie in der Struktur PRINTDLG bei den Flags PD_COLLATE gesetzt haben (pd.Flags & PD_COLLATE). Falls nicht werten Sie die Strukturvariable nCopies aus. Ansonsten belassen Sie es bei einer einmaligen Kopie.

Jetzt können Sie mit StartPage() den Druckertreiber darüber informieren, dass dieser die Daten für eine neue Seite entgegennehmen soll. Außerdem sollte bei dieser Funktion nach jedem Seitenumbruch die Funktion EndPage() aufgerufen werden.

In den nächsten Zeilen lesen wir nun Zeile für Zeile den Text aus und geben diesen auch Zeile für Zeile mit der Funktion TextOut() auf dem Drucker aus:

TextOut ( pd.hDC, 0, yChar * iLine, pstrBuffer,
          (int) SendMessage (hwndEdit, EM_GETLINE,
          (WPARAM) iLineNum, (LPARAM) pstrBuffer) );

Mit der Nachricht EM_GETLINE kopieren Sie eine Zeile vom Editfeld in einen speziellen Puffer.

Wurde eine komplette Seite ausgedruckt (iLine < iLinesPerPage), wird die Funktion EndPage() aufgerufen. Damit wird der Drucker informiert, dass eine komplette Seite ausgedruckt wurde. EndPage() wird auch aufgerufen, wenn alle Zeilen gedruckt wurden (iLineNum > iTotalLines). Ob jetzt noch weitere Seiten gedruckt werden, hängt wieder von den drei verschachtelten for-Schleifen ab. D.h. wurde die gewünschte Anzahl von Kopien und Seiten gedruckt.

Am Ende des Druckvorgangs wird die Funktion EndDoc() aufgerufen. Damit beenden Sie den Druckauftrag, den Sie mit StartDoc() gestartet haben.

Hinweis
 

Dem Listing fehlt noch eine Funktion, die den Abbruch eines Druckerauftrag wenn gar kein Drucker vorhanden ist, abfängt. Diese können Sie ja zur Übung selbst erstellen. Ein Tipp: Sehen Sie sich dazu die Funktionen CreateDialog() und SetAbortProc() an.

9.3. Drucken kurz zusammengefasst            zurück  zum Inhaltsverzeichnis

Den Druckvorgang kann man also folgendermaßen zusammenfassen:

Weiter mit Kapitel 10: Weiterführendes            zum Inhaltsverzeichnis