Proper support for icons in FLTK (upstream STR 2816).


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4898 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/fltk/src/Fl_win32.cxx b/common/fltk/src/Fl_win32.cxx
index 7fc639e..eaae51d 100644
--- a/common/fltk/src/Fl_win32.cxx
+++ b/common/fltk/src/Fl_win32.cxx
@@ -1802,6 +1802,8 @@
   );
   if (lab) free(lab);
 
+  x->set_icons();
+
   if (w->flags() & Fl_Widget::FULLSCREEN) {
   /* We need to make sure that the fullscreen is created on the
      default monitor, ie the desktop where the shortcut is located
@@ -2028,6 +2030,210 @@
 
 ////////////////////////////////////////////////////////////////
 
+static HICON image_to_icon(const Fl_RGB_Image *image, bool is_icon=true,
+                           int hotx = 0, int hoty = 0) {
+  BITMAPV5HEADER bi;
+  HBITMAP bitmap, mask;
+  DWORD *bits;
+  HICON icon;
+
+  if (!is_icon) {
+    if ((hotx < 0) || (hotx >= image->w()))
+      return NULL;
+    if ((hoty < 0) || (hoty >= image->h()))
+      return NULL;
+  }
+
+  memset(&bi, 0, sizeof(BITMAPV5HEADER));
+
+  bi.bV5Size        = sizeof(BITMAPV5HEADER);
+  bi.bV5Width       = image->w();
+  bi.bV5Height      = -image->h(); // Negative for top-down
+  bi.bV5Planes      = 1;
+  bi.bV5BitCount    = 32;
+  bi.bV5Compression = BI_BITFIELDS;
+  bi.bV5RedMask     = 0x00FF0000;
+  bi.bV5GreenMask   = 0x0000FF00;
+  bi.bV5BlueMask    = 0x000000FF;
+  bi.bV5AlphaMask   = 0xFF000000;
+
+  HDC hdc;
+
+  hdc = GetDC(NULL);
+  bitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+  ReleaseDC(NULL, hdc);
+
+  if (bits == NULL)
+    return NULL;
+
+  const uchar *i = (const uchar*)*image->data();
+  for (int y = 0;y < image->h();y++) {
+    for (int x = 0;x < image->w();x++) {
+      switch (image->d()) {
+      case 1:
+        *bits = (0xff<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
+        break;
+      case 2:
+        *bits = (i[1]<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
+        break;
+      case 3:
+        *bits = (0xff<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
+        break;
+      case 4:
+        *bits = (i[3]<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
+        break;
+      }
+      i += image->d();
+      bits++;
+    }
+    i += image->ld();
+  }
+
+  // A mask bitmap is still needed even though it isn't used
+  mask = CreateBitmap(image->w(),image->h(),1,1,NULL);
+  if (mask == NULL) {
+    DeleteObject(bitmap);
+    return NULL;
+  }
+
+  ICONINFO ii;
+
+  ii.fIcon    = is_icon;
+  ii.xHotspot = hotx;
+  ii.yHotspot = hoty;
+  ii.hbmMask  = mask;
+  ii.hbmColor = bitmap;
+
+  icon = CreateIconIndirect(&ii);
+
+  DeleteObject(bitmap);
+  DeleteObject(mask);
+
+  if (icon == NULL)
+    return NULL;
+
+  return icon;
+}
+
+////////////////////////////////////////////////////////////////
+
+static HICON default_big_icon = NULL;
+static HICON default_small_icon = NULL;
+
+const Fl_RGB_Image *find_best_icon(int ideal_width, 
+                                   const Fl_RGB_Image *icons[], int count) {
+  const Fl_RGB_Image *best;
+
+  best = NULL;
+
+  for (int i = 0;i < count;i++) {
+    if (best == NULL)
+      best = icons[i];
+    else {
+      if (best->w() < ideal_width) {
+        if (icons[i]->w() > best->w())
+          best = icons[i];
+      } else {
+        if ((icons[i]->w() >= ideal_width) &&
+            (icons[i]->w() < best->w()))
+          best = icons[i];
+      }
+    }
+  }
+
+  return best;
+}
+
+void Fl_X::set_default_icons(const Fl_RGB_Image *icons[], int count) {
+  const Fl_RGB_Image *best_big, *best_small;
+
+  if (default_big_icon != NULL)
+    DestroyIcon(default_big_icon);
+  if (default_small_icon != NULL)
+    DestroyIcon(default_small_icon);
+
+  best_big = find_best_icon(GetSystemMetrics(SM_CXICON), icons, count);
+  best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON), icons, count);
+
+  if (best_big != NULL)
+    default_big_icon = image_to_icon(best_big);
+  else
+    default_big_icon = NULL;
+
+  if (best_small != NULL)
+    default_small_icon = image_to_icon(best_small);
+  else
+    default_small_icon = NULL;
+}
+
+void Fl_X::set_default_icons(HICON big_icon, HICON small_icon) {
+  if (default_big_icon != NULL)
+    DestroyIcon(default_big_icon);
+  if (default_small_icon != NULL)
+    DestroyIcon(default_small_icon);
+
+  if (big_icon != NULL)
+    default_big_icon = CopyIcon(big_icon);
+  if (small_icon != NULL)
+    default_small_icon = CopyIcon(small_icon);
+}
+
+void Fl_X::set_icons() {
+  HICON big_icon, small_icon;
+
+  big_icon = NULL;
+  small_icon = NULL;
+
+  if (w->icon_->count) {
+    const Fl_RGB_Image *best_big, *best_small;
+
+    best_big = find_best_icon(GetSystemMetrics(SM_CXICON),
+                              (const Fl_RGB_Image **)w->icon_->icons,
+                              w->icon_->count);
+    best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON),
+                                (const Fl_RGB_Image **)w->icon_->icons,
+                                w->icon_->count);
+
+    if (best_big != NULL)
+      big_icon = image_to_icon(best_big);
+    if (best_small != NULL)
+      small_icon = image_to_icon(best_small);
+  } else {
+    big_icon = default_big_icon;
+    small_icon = default_small_icon;
+  }
+
+  if (big_icon != NULL)
+    SendMessage(xid, WM_SETICON, ICON_BIG, (LPARAM)big_icon);
+  if (small_icon != NULL)
+    SendMessage(xid, WM_SETICON, ICON_SMALL, (LPARAM)small_icon);
+
+  if (w->icon_->count) {
+    if (big_icon != NULL)
+      DestroyIcon(big_icon);
+    if (small_icon != NULL)
+      DestroyIcon(small_icon);
+  }
+}
+
+void Fl_Window::default_icons(HICON big_icon, HICON small_icon) {
+  Fl_X::set_default_icons(big_icon, small_icon);
+}
+
+void Fl_Window::icons(HICON big_icon, HICON small_icon) {
+  free_icons();
+
+  if (big_icon != NULL)
+    icon_->big_icon = CopyIcon(big_icon);
+  if (small_icon != NULL)
+    icon_->small_icon = CopyIcon(small_icon);
+
+  if (i)
+    i->set_icons();
+}
+
+////////////////////////////////////////////////////////////////
+
 #ifndef IDC_HAND
 #  define IDC_HAND  MAKEINTRESOURCE(32649)
 #endif // !IDC_HAND
@@ -2084,81 +2290,9 @@
 }
 
 int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) {
-  BITMAPV5HEADER bi;
-  HBITMAP bitmap, mask;
-  DWORD *bits;
   HCURSOR new_cursor;
 
-  if ((hotx < 0) || (hotx >= image->w()))
-    return 0;
-  if ((hoty < 0) || (hoty >= image->h()))
-    return 0;
-
-  memset(&bi, 0, sizeof(BITMAPV5HEADER));
-
-  bi.bV5Size        = sizeof(BITMAPV5HEADER);
-  bi.bV5Width       = image->w();
-  bi.bV5Height      = -image->h(); // Negative for top-down
-  bi.bV5Planes      = 1;
-  bi.bV5BitCount    = 32;
-  bi.bV5Compression = BI_BITFIELDS;
-  bi.bV5RedMask     = 0x00FF0000;
-  bi.bV5GreenMask   = 0x0000FF00;
-  bi.bV5BlueMask    = 0x000000FF;
-  bi.bV5AlphaMask   = 0xFF000000;
-
-  HDC hdc;
-
-  hdc = GetDC(NULL);
-  bitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
-  ReleaseDC(NULL, hdc);
-
-  if (bits == NULL)
-    return 0;
-
-  const uchar *i = (const uchar*)*image->data();
-  for (int y = 0;y < image->h();y++) {
-    for (int x = 0;x < image->w();x++) {
-      switch (image->d()) {
-      case 1:
-        *bits = (0xff<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
-        break;
-      case 2:
-        *bits = (i[1]<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
-        break;
-      case 3:
-        *bits = (0xff<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
-        break;
-      case 4:
-        *bits = (i[3]<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
-        break;
-      }
-      i += image->d();
-      bits++;
-    }
-    i += image->ld();
-  }
-
-  // A mask bitmap is still needed even though it isn't used
-  mask = CreateBitmap(image->w(),image->h(),1,1,NULL);
-  if (mask == NULL) {
-    DeleteObject(bitmap);
-    return 0;
-  }
-
-  ICONINFO ii;
-
-  ii.fIcon    = FALSE;
-  ii.xHotspot = hotx;
-  ii.yHotspot = hoty;
-  ii.hbmMask  = mask;
-  ii.hbmColor = bitmap;
-
-  new_cursor = CreateIconIndirect(&ii);
-
-  DeleteObject(bitmap);
-  DeleteObject(mask);
-
+  new_cursor = image_to_icon(image, false, hotx, hoty);
   if (new_cursor == NULL)
     return 0;