Make the `hold marker' in Guess accessible from the keyboard (`H' key, for want
[sgt/puzzles] / windows.c
index 30d223d..0cf8148 100644 (file)
--- a/windows.c
+++ b/windows.c
@@ -1,14 +1,5 @@
 /*
  * windows.c: Windows front end for my puzzle collection.
- * 
- * TODO:
- * 
- *  - Figure out what to do if a puzzle requests a size bigger than
- *    the screen will take. In principle we could put scrollbars in
- *    the window, although that would be pretty horrid. Another
- *    option is to detect in advance that this will be a problem -
- *    we can probably tell this using midend_size() before actually
- *    generating the puzzle - and simply refuse to do it.
  */
 
 #include <windows.h>
@@ -90,6 +81,12 @@ struct cfg_aux {
     int ctlid;
 };
 
+struct blitter {
+    HBITMAP bitmap;
+    frontend *fe;
+    int x, y, w, h;
+};
+
 struct frontend {
     midend_data *me;
     HWND hwnd, statusbar, cfgbox;
@@ -149,6 +146,82 @@ void status_bar(frontend *fe, char *text)
     }
 }
 
+blitter *blitter_new(int w, int h)
+{
+    blitter *bl = snew(blitter);
+
+    memset(bl, 0, sizeof(blitter));
+    bl->w = w;
+    bl->h = h;
+    bl->bitmap = 0;
+
+    return bl;
+}
+
+void blitter_free(blitter *bl)
+{
+    if (bl->bitmap) DeleteObject(bl->bitmap);
+    sfree(bl);
+}
+
+static void blitter_mkbitmap(frontend *fe, blitter *bl)
+{
+    HDC hdc = GetDC(fe->hwnd);
+    bl->bitmap = CreateCompatibleBitmap(hdc, bl->w, bl->h);
+    ReleaseDC(fe->hwnd, hdc);
+}
+
+/* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */
+
+void blitter_save(frontend *fe, blitter *bl, int x, int y)
+{
+    HDC hdc_win, hdc_blit;
+    HBITMAP prev_blit;
+
+    if (!bl->bitmap) blitter_mkbitmap(fe, bl);
+
+    bl->x = x; bl->y = y;
+
+    hdc_win = GetDC(fe->hwnd);
+    hdc_blit = CreateCompatibleDC(hdc_win);
+    if (!hdc_blit) fatal("hdc_blit failed: 0x%x", GetLastError());
+
+    prev_blit = SelectObject(hdc_blit, bl->bitmap);
+    if (prev_blit == NULL || prev_blit == HGDI_ERROR)
+        fatal("SelectObject for hdc_main failed: 0x%x", GetLastError());
+
+    if (!BitBlt(hdc_blit, 0, 0, bl->w, bl->h,
+                fe->hdc_bm, x, y, SRCCOPY))
+        fatal("BitBlt failed: 0x%x", GetLastError());
+
+    SelectObject(hdc_blit, prev_blit);
+    DeleteDC(hdc_blit);
+    ReleaseDC(fe->hwnd, hdc_win);
+}
+
+void blitter_load(frontend *fe, blitter *bl, int x, int y)
+{
+    HDC hdc_win, hdc_blit;
+    HBITMAP prev_blit;
+
+    assert(bl->bitmap); /* we should always have saved before loading */
+
+    if (x == BLITTER_FROMSAVED) x = bl->x;
+    if (y == BLITTER_FROMSAVED) y = bl->y;
+
+    hdc_win = GetDC(fe->hwnd);
+    hdc_blit = CreateCompatibleDC(hdc_win);
+
+    prev_blit = SelectObject(hdc_blit, bl->bitmap);
+
+    BitBlt(fe->hdc_bm, x, y, bl->w, bl->h,
+           hdc_blit, 0, 0, SRCCOPY);
+
+    SelectObject(hdc_blit, prev_blit);
+    DeleteDC(hdc_blit);
+    ReleaseDC(fe->hwnd, hdc_win);
+}
+
 void frontend_default_colour(frontend *fe, float *output)
 {
     DWORD c = GetSysColor(COLOR_MENU); /* ick */
@@ -257,6 +330,24 @@ void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour)
     SelectObject(fe->hdc_bm, oldpen);
 }
 
+void draw_circle(frontend *fe, int cx, int cy, int radius,
+                 int fill, int colour)
+{
+    if (fill) {
+       HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]);
+       HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]);
+       Ellipse(fe->hdc_bm, cx - radius, cy - radius,
+               cx + radius + 1, cy + radius + 1);
+       SelectObject(fe->hdc_bm, oldbrush);
+       SelectObject(fe->hdc_bm, oldpen);
+    } else {
+       HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]);
+       MoveToEx(fe->hdc_bm, cx + radius, cy, NULL);
+       AngleArc(fe->hdc_bm, cx, cy, radius, 0.0F, 360.0F);
+       SelectObject(fe->hdc_bm, oldpen);
+    }    
+}
+
 void draw_polygon(frontend *fe, int *coords, int npoints,
                   int fill, int colour)
 {
@@ -522,13 +613,23 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
                              r.right - r.left, r.bottom - r.top,
                              NULL, NULL, inst, NULL);
 
-    if (midend_wants_statusbar(fe->me))
+    if (midend_wants_statusbar(fe->me)) {
+       RECT sr;
        fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, "ooh",
                                       WS_CHILD | WS_VISIBLE,
                                       0, 0, 0, 0, /* status bar does these */
                                       fe->hwnd, NULL, inst, NULL);
-    else
+       /*
+        * Now resize the window to take account of the status bar.
+        */
+       GetWindowRect(fe->statusbar, &sr);
+       GetWindowRect(fe->hwnd, &r);
+       SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left,
+                    r.bottom - r.top + sr.bottom - sr.top,
+                    SWP_NOMOVE | SWP_NOZORDER);
+    } else {
        fe->statusbar = NULL;
+    }
 
     {
        HMENU bar = CreateMenu();