de_win.c raw

   1  /*
   2   * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
   3   *
   4   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   5   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   6   *
   7   * Permission is hereby granted to use or copy this program
   8   * for any purpose, provided the above notices are retained on all copies.
   9   * Permission to modify the code and to distribute modified code is granted,
  10   * provided the above notices are retained, and a notice that the code was
  11   * modified is included with the above copyright notice.
  12   */
  13  
  14  /*
  15   * The Windows-specific part of `de`.  This has been started as the generic
  16   * Windows application template but its significant parts did not survive
  17   * to the final version.
  18   */
  19  
  20  #if defined(__CYGWIN__) || defined(__MINGW32__)                 \
  21      || (defined(__NT__) && defined(__386__)) || defined(_WIN32) \
  22      || defined(WIN32)
  23  
  24  #  ifndef WIN32_LEAN_AND_MEAN
  25  #    define WIN32_LEAN_AND_MEAN 1
  26  #  endif
  27  #  define NOSERVICE
  28  #  include <windows.h>
  29  
  30  #  include <ctype.h>
  31  
  32  #  include "gc.h"
  33  #  include "gc/cord.h"
  34  
  35  #  include "de_win.h"
  36  
  37  int LINES = 0;
  38  int COLS = 0;
  39  
  40  #  define szAppName TEXT("DE")
  41  
  42  static HWND hwnd;
  43  
  44  void
  45  de_error(const char *s)
  46  {
  47    (void)MessageBoxA(hwnd, s, "Demonstration Editor",
  48                      MB_ICONINFORMATION | MB_OK);
  49    InvalidateRect(hwnd, NULL, TRUE);
  50  }
  51  
  52  static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,
  53                                  LPARAM lParam);
  54  
  55  int APIENTRY
  56  WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR command_line,
  57          int nCmdShow)
  58  {
  59    MSG msg;
  60    WNDCLASS wndclass;
  61    HACCEL hAccel;
  62  
  63    GC_set_find_leak(0);
  64    GC_INIT();
  65  #  ifndef NO_INCREMENTAL
  66    GC_enable_incremental();
  67  #  endif
  68  #  if defined(CPPCHECK)
  69    GC_noop1((GC_word)(GC_uintptr_t)(&WinMain));
  70  #  endif
  71  
  72    if (!hPrevInstance) {
  73      wndclass.style = CS_HREDRAW | CS_VREDRAW;
  74      wndclass.lpfnWndProc = WndProc;
  75      wndclass.cbClsExtra = 0;
  76      wndclass.cbWndExtra = DLGWINDOWEXTRA;
  77      wndclass.hInstance = hInstance;
  78      wndclass.hIcon = LoadIcon(hInstance, szAppName);
  79      wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  80      wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  81      wndclass.lpszMenuName = TEXT("DE");
  82      wndclass.lpszClassName = szAppName;
  83  
  84      if (RegisterClass(&wndclass) == 0) {
  85        de_error("RegisterClass error");
  86        return 0;
  87      }
  88    }
  89  
  90    /* Empirically, the command line does not include the command name... */
  91    if (command_line == 0 || *command_line == 0) {
  92      de_error("File name argument required");
  93      return 0;
  94    } else {
  95      char *p = command_line;
  96  
  97      while (*p != 0 && !isspace(*(unsigned char *)p))
  98        p++;
  99      arg_file_name
 100          = CORD_to_char_star(CORD_substr(command_line, 0, p - command_line));
 101    }
 102  
 103    hwnd = CreateWindow(
 104        szAppName, TEXT("Demonstration Editor") /* `lpWindowName` */,
 105        WS_OVERLAPPEDWINDOW | WS_CAPTION /* `dwStyle` */,
 106        CW_USEDEFAULT /* `x` */, 0 /* `y` */, CW_USEDEFAULT /* `nWidth` */,
 107        0 /* `nHeight` */, NULL /* `hWndParent` */, NULL /* `hMenu` */,
 108        hInstance, NULL /* `lpParam` */);
 109    if (NULL == hwnd) {
 110      de_error("CreateWindow error");
 111      return 0;
 112    }
 113  
 114    ShowWindow(hwnd, nCmdShow);
 115  
 116    hAccel = LoadAccelerators(hInstance, szAppName);
 117  
 118    while (GetMessage(&msg, NULL, 0, 0)) {
 119      if (!TranslateAccelerator(hwnd, hAccel, &msg)) {
 120        TranslateMessage(&msg);
 121        DispatchMessage(&msg);
 122      }
 123    }
 124    return (int)msg.wParam;
 125  }
 126  
 127  /* Return the argument with all control characters replaced by blanks. */
 128  static char *
 129  plain_chars(const char *text, size_t len)
 130  {
 131    char *result = (char *)GC_MALLOC_ATOMIC(len + 1);
 132    size_t i;
 133  
 134    if (NULL == result)
 135      return NULL;
 136    for (i = 0; i < len; i++) {
 137      if (iscntrl(((unsigned char *)text)[i])) {
 138        result[i] = ' ';
 139      } else {
 140        result[i] = text[i];
 141      }
 142    }
 143    result[len] = '\0';
 144    return result;
 145  }
 146  
 147  /*
 148   * Return the argument with all non-control-characters replaced by blank,
 149   * and all control characters `c` replaced by `c + 64`.
 150   */
 151  static char *
 152  control_chars(const char *text, size_t len)
 153  {
 154    char *result = (char *)GC_MALLOC_ATOMIC(len + 1);
 155    size_t i;
 156  
 157    if (NULL == result)
 158      return NULL;
 159    for (i = 0; i < len; i++) {
 160      if (iscntrl(((unsigned char *)text)[i])) {
 161        result[i] = (char)(text[i] + 0x40);
 162      } else {
 163        result[i] = ' ';
 164      }
 165    }
 166    result[len] = '\0';
 167    return result;
 168  }
 169  
 170  static int char_width;
 171  static int char_height;
 172  
 173  static void
 174  get_line_rect(int line_arg, int win_width, RECT *rectp)
 175  {
 176    rectp->top = line_arg * (LONG)char_height;
 177    rectp->bottom = rectp->top + char_height;
 178    rectp->left = 0;
 179    rectp->right = win_width;
 180  }
 181  
 182  /* A flag whether the caret is currently visible. */
 183  static int caret_visible = 0;
 184  
 185  /* A flag whether the screen has been painted at least once. */
 186  static int screen_was_painted = 0;
 187  
 188  static void update_cursor(void);
 189  
 190  static INT_PTR CALLBACK
 191  AboutBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 192  {
 193    (void)lParam;
 194    switch (message) {
 195    case WM_INITDIALOG:
 196      SetFocus(GetDlgItem(hDlg, IDOK));
 197      break;
 198  
 199    case WM_COMMAND:
 200      switch (wParam) {
 201      case IDOK:
 202        EndDialog(hDlg, TRUE);
 203        break;
 204      }
 205      break;
 206  
 207    case WM_CLOSE:
 208      EndDialog(hDlg, TRUE);
 209      return TRUE;
 210    }
 211    return FALSE;
 212  }
 213  
 214  static LRESULT CALLBACK
 215  WndProc(HWND hwnd_arg, UINT message, WPARAM wParam, LPARAM lParam)
 216  {
 217    static HINSTANCE hInstance;
 218    HDC dc;
 219    PAINTSTRUCT ps;
 220    RECT client_area;
 221    RECT this_line;
 222    RECT dummy;
 223    TEXTMETRIC tm;
 224    int i;
 225    int id;
 226  
 227    switch (message) {
 228    case WM_CREATE:
 229      hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
 230      dc = GetDC(hwnd_arg);
 231      SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT));
 232      GetTextMetrics(dc, &tm);
 233      ReleaseDC(hwnd_arg, dc);
 234      char_width = tm.tmAveCharWidth;
 235      char_height = tm.tmHeight + tm.tmExternalLeading;
 236      GetClientRect(hwnd_arg, &client_area);
 237      COLS = (client_area.right - client_area.left) / char_width;
 238      LINES = (client_area.bottom - client_area.top) / char_height;
 239      generic_init();
 240      return 0;
 241  
 242    case WM_CHAR:
 243      if (wParam == QUIT) {
 244        SendMessage(hwnd_arg, WM_CLOSE, 0, 0L);
 245      } else {
 246        do_command((int)wParam);
 247      }
 248      return 0;
 249  
 250    case WM_SETFOCUS:
 251      CreateCaret(hwnd_arg, NULL, char_width, char_height);
 252      ShowCaret(hwnd_arg);
 253      caret_visible = 1;
 254      update_cursor();
 255      return 0;
 256  
 257    case WM_KILLFOCUS:
 258      HideCaret(hwnd_arg);
 259      DestroyCaret();
 260      caret_visible = 0;
 261      return 0;
 262  
 263    case WM_LBUTTONUP:
 264      {
 265        unsigned xpos = LOWORD(lParam); /*< from left */
 266        unsigned ypos = HIWORD(lParam); /*< from top */
 267  
 268        set_position(xpos / (unsigned)char_width, ypos / (unsigned)char_height);
 269      }
 270      return 0;
 271  
 272    case WM_COMMAND:
 273      id = LOWORD(wParam);
 274      if (id & EDIT_CMD_FLAG) {
 275        if (id & REPEAT_FLAG)
 276          do_command(REPEAT);
 277        do_command(CHAR_CMD(id));
 278        return 0;
 279      }
 280      switch (id) {
 281      case IDM_FILEEXIT:
 282        SendMessage(hwnd_arg, WM_CLOSE, 0, 0L);
 283        return 0;
 284      case IDM_HELPABOUT:
 285        if (DialogBox(hInstance, TEXT("ABOUTBOX"), hwnd_arg, AboutBoxCallback)) {
 286          InvalidateRect(hwnd_arg, NULL, TRUE);
 287        }
 288        return 0;
 289      case IDM_HELPCONTENTS:
 290        de_error("Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n"
 291                 "Undo: ^U    Write: ^W   Quit:^D  Repeat count: ^R[n]\n"
 292                 "Top: ^T   Locate (search, find): ^L text ^L\n");
 293        return 0;
 294      }
 295      break;
 296  
 297    case WM_CLOSE:
 298      DestroyWindow(hwnd_arg);
 299      return 0;
 300  
 301    case WM_DESTROY:
 302      PostQuitMessage(0);
 303      GC_win32_free_heap();
 304      return 0;
 305  
 306    case WM_PAINT:
 307      dc = BeginPaint(hwnd_arg, &ps);
 308      GetClientRect(hwnd_arg, &client_area);
 309      COLS = (client_area.right - client_area.left) / char_width;
 310      LINES = (client_area.bottom - client_area.top) / char_height;
 311      SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT));
 312      for (i = 0; i < LINES; i++) {
 313        get_line_rect(i, client_area.right, &this_line);
 314        if (IntersectRect(&dummy, &this_line, &ps.rcPaint)) {
 315          CORD raw_line = (CORD)retrieve_screen_line(i);
 316          size_t len = CORD_len(raw_line);
 317          const char *text = CORD_to_char_star(raw_line);
 318          /* May contain embedded NUL characters. */
 319          char *plain = plain_chars(text, len);
 320          char *blanks = CORD_to_char_star(CORD_chars(' ', COLS - len));
 321          char *control = control_chars(text, len);
 322          if (NULL == plain || NULL == control)
 323            de_error("Out of memory!");
 324  
 325  #  define RED RGB(255, 0, 0)
 326  
 327          SetBkMode(dc, OPAQUE);
 328          SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT));
 329  
 330          if (plain != NULL)
 331            TextOutA(dc, this_line.left, this_line.top, plain, (int)len);
 332          TextOutA(dc, this_line.left + (int)len * char_width, this_line.top,
 333                   blanks, (int)(COLS - len));
 334          SetBkMode(dc, TRANSPARENT);
 335          SetTextColor(dc, RED);
 336          if (control != NULL)
 337            TextOutA(dc, this_line.left, this_line.top, control,
 338                     (int)strlen(control));
 339        }
 340      }
 341      EndPaint(hwnd_arg, &ps);
 342      screen_was_painted = 1;
 343      return 0;
 344    }
 345    return DefWindowProc(hwnd_arg, message, wParam, lParam);
 346  }
 347  
 348  static int last_col;
 349  static int last_line;
 350  
 351  void
 352  move_cursor(int c, int l)
 353  {
 354    last_col = c;
 355    last_line = l;
 356  
 357    if (caret_visible)
 358      update_cursor();
 359  }
 360  
 361  static void
 362  update_cursor(void)
 363  {
 364    SetCaretPos(last_col * char_width, last_line * char_height);
 365    ShowCaret(hwnd);
 366  }
 367  
 368  void
 369  invalidate_line(int i)
 370  {
 371    RECT line_r;
 372  
 373    if (!screen_was_painted) {
 374      /*
 375       * Invalidating a rectangle before painting seems result in a major
 376       * performance problem.
 377       */
 378      return;
 379    }
 380    get_line_rect(i, COLS * char_width, &line_r);
 381    InvalidateRect(hwnd, &line_r, FALSE);
 382  }
 383  
 384  #else
 385  
 386  /*
 387   * ANSI C does not allow translation units to be empty.
 388   * So we guarantee this one is nonempty.
 389   */
 390  extern int GC_quiet;
 391  
 392  #endif /* !WIN32 */
 393