Use the more modern API for getting icons

Qt's fromHBITMAP is totally broken so convert the bitmap to an HICON
ourselves first.
This commit is contained in:
Kovid Goyal 2023-01-04 21:44:44 +05:30
parent 8059813337
commit 42c283bdee
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 22 additions and 41 deletions

View File

@ -4,13 +4,14 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
import os
import re import re
import sys import sys
from qt.core import QBuffer, QByteArray, QIODevice, QPixmap, Qt from qt.core import QBuffer, QByteArray, QIODevice, QPixmap, Qt
from calibre.gui2 import must_use_qt from calibre.gui2 import must_use_qt
from calibre.utils.winreg.default_programs import split_commandline from calibre.utils.winreg.default_programs import split_commandline
from calibre_extensions import winutil, progress_indicator from calibre_extensions import progress_indicator, winutil
ICON_SIZE = 256 ICON_SIZE = 256
@ -76,6 +77,7 @@ def load_icon_resource(icon_resource, as_data=False, size=ICON_SIZE):
def load_icon_for_file(path: str, as_data=False, size=ICON_SIZE): def load_icon_for_file(path: str, as_data=False, size=ICON_SIZE):
path = os.path.abspath(path)
try: try:
hicon = winutil.get_icon_for_file(path) hicon = winutil.get_icon_for_file(path)
except Exception: except Exception:

View File

@ -1104,46 +1104,9 @@ load_icons(PyObject *self, PyObject *args) {
return ans; return ans;
} }
_COM_SMARTPTR_TYPEDEF(IImageList, __uuidof(IImageList));
static HICON
get_icon_at_index(int shilsize, int index) {
IImageListPtr spiml;
HRESULT hr = SHGetImageList(shilsize, IID_PPV_ARGS(&spiml));
HICON hico = NULL;
if (SUCCEEDED(hr)) spiml->GetIcon(index, ILD_TRANSPARENT | ILD_IMAGE, &hico);
spiml->Release();
return hico;
}
static PyObject* static PyObject*
get_icon_for_file(PyObject *self, PyObject *args) { get_icon_for_file(PyObject *self, PyObject *args) {
wchar_raii path; wchar_raii path;
if (!PyArg_ParseTuple(args, "O&", py_to_wchar_no_none, &path)) return NULL;
scoped_com_initializer com;
if (!com.succeeded()) { PyErr_SetString(PyExc_OSError, "Failed to initialize COM"); return NULL; }
SHFILEINFO fi = {0};
DWORD_PTR res;
Py_BEGIN_ALLOW_THREADS
res = SHGetFileInfoW(path.ptr(), 0, &fi, sizeof(fi), SHGFI_SYSICONINDEX);
Py_END_ALLOW_THREADS
if (!res) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, ERROR_RESOURCE_TYPE_NOT_FOUND, PyTuple_GET_ITEM(args, 0));
HICON icon;
#define R(shil) { \
Py_BEGIN_ALLOW_THREADS \
icon = get_icon_at_index(shil, fi.iIcon); \
Py_END_ALLOW_THREADS \
if (icon) return (PyObject*)Handle_create(icon, IconHandle); \
}
R(SHIL_JUMBO); R(SHIL_EXTRALARGE); R(SHIL_LARGE); R(SHIL_SYSSMALL); R(SHIL_SMALL);
#undef R
return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, ERROR_RESOURCE_TYPE_NOT_FOUND, PyTuple_GET_ITEM(args, 0));
}
static PyObject*
get_bitmap_for_file(PyObject *self, PyObject *args) {
wchar_raii path;
long width = 256, height = 256; long width = 256, height = 256;
if (!PyArg_ParseTuple(args, "O&|ll", py_to_wchar_no_none, &path, &width, &height)) return NULL; if (!PyArg_ParseTuple(args, "O&|ll", py_to_wchar_no_none, &path, &width, &height)) return NULL;
scoped_com_initializer com; scoped_com_initializer com;
@ -1159,13 +1122,30 @@ get_bitmap_for_file(PyObject *self, PyObject *args) {
SIZE size = { width, height }; SIZE size = { width, height };
HBITMAP hbmp; HBITMAP hbmp;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
hr = pImageFactory->GetImage(size, SIIGBF_BIGGERSIZEOK, &hbmp); hr = pImageFactory->GetImage(size, SIIGBF_BIGGERSIZEOK | SIIGBF_SCALEUP, &hbmp);
pImageFactory->Release(); pImageFactory->Release();
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
return error_from_hresult(hr, "Failed to get image from shell item"); return error_from_hresult(hr, "Failed to get image from shell item");
} }
return (PyObject*)Handle_create(hbmp, BitmapHandle); BITMAP bmp;
if (GetObject(hbmp, sizeof( BITMAP ), &bmp) == 0) {
DeleteObject(hbmp);
PyErr_SetString(PyExc_OSError, "Failed to load bitmap data from HBITMAP");
return NULL;
}
HBITMAP hbmMask = CreateCompatibleBitmap(GetDC(NULL), bmp.bmWidth, bmp.bmHeight);
ICONINFO ii = {0};
ii.fIcon = TRUE;
ii.hbmColor = hbmp;
ii.hbmMask = hbmMask;
HICON hIcon = CreateIconIndirect(&ii);
DeleteObject(hbmp); DeleteObject(hbmMask);
if (hIcon == NULL) {
PyErr_SetFromWindowsErr(GetLastError());
return NULL;
}
return (PyObject*)Handle_create(hIcon, IconHandle);
} // }}} } // }}}
// Boilerplate {{{ // Boilerplate {{{
@ -1190,7 +1170,6 @@ static PyMethodDef winutil_methods[] = {
M(load_library, METH_VARARGS), M(load_library, METH_VARARGS),
M(load_icons, METH_VARARGS), M(load_icons, METH_VARARGS),
M(get_icon_for_file, METH_VARARGS), M(get_icon_for_file, METH_VARARGS),
M(get_bitmap_for_file, METH_VARARGS),
M(parse_cmdline, METH_VARARGS), M(parse_cmdline, METH_VARARGS),
M(write_file, METH_VARARGS), M(write_file, METH_VARARGS),
M(wait_named_pipe, METH_VARARGS), M(wait_named_pipe, METH_VARARGS),