uiDrawPath *uiDrawNewPath(uiDrawFillMode fillmode) { uiDrawPath *p; HRESULT hr; p = uiNew(uiDrawPath); hr = ID2D1Factory_CreatePathGeometry(d2dfactory, &(p->path)); if (hr != S_OK) logHRESULT("error creating path in uiDrawNewPath()", hr); hr = ID2D1PathGeometry_Open(p->path, &(p->sink)); if (hr != S_OK) logHRESULT("error opening path in uiDrawNewPath()", hr); switch (fillmode) { case uiDrawFillModeWinding: ID2D1GeometrySink_SetFillMode(p->sink, D2D1_FILL_MODE_WINDING); break; case uiDrawFillModeAlternate: ID2D1GeometrySink_SetFillMode(p->sink, D2D1_FILL_MODE_ALTERNATE); break; } return p; }
static void familyChanged(struct fontDialog *f) { LRESULT pos; BOOL selected; IDWriteFontFamily *family; IDWriteFont *font, *matchFont; DWRITE_FONT_WEIGHT weight; DWRITE_FONT_STYLE style; DWRITE_FONT_STRETCH stretch; UINT32 i, n; UINT32 matching; WCHAR *label; HRESULT hr; selected = cbGetCurSel(f->familyCombobox, &pos); if (!selected) // on deselect, do nothing return; f->curFamily = pos; family = (IDWriteFontFamily *) cbGetItemData(f->familyCombobox, (WPARAM) (f->curFamily)); // for the nearest style match // when we select a new family, we want the nearest style to the previously selected one to be chosen // this is how the Choose Font sample does it hr = family->GetFirstMatchingFont( f->weight, f->stretch, f->style, &matchFont); if (hr != S_OK) logHRESULT(L"error finding first matching font to previous style in font dialog", hr); // we can't just compare pointers; a "newly created" object comes out // the Choose Font sample appears to do this instead weight = matchFont->GetWeight(); style = matchFont->GetStyle(); stretch = matchFont->GetStretch(); matchFont->Release(); // TODO test mutliple streteches; all the fonts I have have only one stretch value? wipeStylesBox(f); n = family->GetFontCount(); matching = 0; // a safe/suitable default just in case for (i = 0; i < n; i++) { hr = family->GetFont(i, &font); if (hr != S_OK) logHRESULT(L"error getting font for filling styles box", hr); label = fontStyleName(f->fc, font); pos = cbAddString(f->styleCombobox, label); uiFree(label); cbSetItemData(f->styleCombobox, (WPARAM) pos, (LPARAM) font); if (font->GetWeight() == weight && font->GetStyle() == style && font->GetStretch() == stretch) matching = i; } // and now, load the match cbSetCurSel(f->styleCombobox, (WPARAM) matching); styleChanged(f); }
// once again, this is based on the Microsoft sample above static void drawOpacitySlider(struct colorDialog *c, ID2D1RenderTarget *rt) { D2D1_SIZE_F size; D2D1_RECT_F rect; D2D1_GRADIENT_STOP stops[2]; ID2D1GradientStopCollection *collection; D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lprop; D2D1_BRUSH_PROPERTIES bprop; ID2D1LinearGradientBrush *brush; double hypot; D2D1_POINT_2F center; HRESULT hr; size = realGetSize(rt); rect.left = 0; rect.top = 0; rect.right = size.width; rect.bottom = size.height * (5.0 / 6.0); // bottommost sixth for arrow drawGrid(rt, &rect); stops[0].position = 0.0; stops[0].color.r = 0.0; stops[0].color.g = 0.0; stops[0].color.b = 0.0; stops[0].color.a = 1.0; stops[1].position = 1.0; stops[1].color.r = 1.0; // this is the XAML color Transparent, as in the source stops[1].color.g = 1.0; stops[1].color.b = 1.0; stops[1].color.a = 0.0; hr = rt->CreateGradientStopCollection(stops, 2, // note that in this case this gamma is explicitly specified by the original D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, &collection); if (hr != S_OK) logHRESULT(L"error creating stop collection for opacity slider gradient", hr); ZeroMemory(&lprop, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES)); lprop.startPoint.x = 0; lprop.startPoint.y = (rect.bottom - rect.top) / 2; lprop.endPoint.x = size.width; lprop.endPoint.y = (rect.bottom - rect.top) / 2; ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); bprop.opacity = 1.0; bprop.transform._11 = 1; bprop.transform._22 = 1; hr = rt->CreateLinearGradientBrush(&lprop, &bprop, collection, &brush); if (hr != S_OK) logHRESULT(L"error creating gradient brush for opacity slider", hr); rt->FillRectangle(&rect, brush); brush->Release(); collection->Release(); // now draw a black arrow center.x = (1 - c->a) * size.width; center.y = size.height; hypot = size.height - rect.bottom; drawArrow(rt, center, hypot); }
// TODO run again when the DPI or the theme changes // TODO properly clean things up here // TODO properly destroy the old lists here too HRESULT uiprivUpdateImageListSize(uiTable *t) { HDC dc; int cxList, cyList; HTHEME theme; SIZE sizeCheck; HRESULT hr; dc = GetDC(t->hwnd); if (dc == NULL) { logLastError(L"GetDC()"); return E_FAIL; } cxList = GetSystemMetrics(SM_CXSMICON); cyList = GetSystemMetrics(SM_CYSMICON); sizeCheck.cx = cxList; sizeCheck.cy = cyList; theme = OpenThemeData(t->hwnd, L"button"); if (theme != NULL) { hr = GetThemePartSize(theme, dc, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, NULL, TS_DRAW, &sizeCheck); if (hr != S_OK) { logHRESULT(L"GetThemePartSize()", hr); return hr; // TODO fall back? } // make sure these checkmarks fit // unthemed checkmarks will by the code above be smaller than cxList/cyList here if (cxList < sizeCheck.cx) cxList = sizeCheck.cx; if (cyList < sizeCheck.cy) cyList = sizeCheck.cy; hr = CloseThemeData(theme); if (hr != S_OK) { logHRESULT(L"CloseThemeData()", hr); return hr; } } // TODO handle errors t->imagelist = ImageList_Create(cxList, cyList, ILC_COLOR32, 1, 1); if (t->imagelist == NULL) { logLastError(L"ImageList_Create()"); return E_FAIL; } // TODO will this return NULL here because it's an initial state? SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->imagelist)); if (ReleaseDC(t->hwnd, dc) == 0) { logLastError(L"ReleaseDC()"); return E_FAIL; } return S_OK; }
static ID2D1Brush *makeRadialBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props) { ID2D1RadialGradientBrush *brush; D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES gprops; ID2D1GradientStopCollection *stops; HRESULT hr; ZeroMemory(&gprops, sizeof (D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES)); gprops.gradientOriginOffset.x = b->X0 - b->X1; gprops.gradientOriginOffset.y = b->Y0 - b->Y1; gprops.center.x = b->X1; gprops.center.y = b->Y1; gprops.radiusX = b->OuterRadius; gprops.radiusY = b->OuterRadius; stops = mkstops(b, rt); hr = ID2D1RenderTarget_CreateRadialGradientBrush(rt, &gprops, props, stops, &brush); if (hr != S_OK) logHRESULT("error creating gradient brush in makeRadialBrush()", hr); ID2D1GradientStopCollection_Release(stops); return (ID2D1Brush *) brush; }
static ID2D1Brush *makeLinearBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props) { ID2D1LinearGradientBrush *brush; D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES gprops; ID2D1GradientStopCollection *stops; HRESULT hr; ZeroMemory(&gprops, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES)); gprops.startPoint.x = b->X0; gprops.startPoint.y = b->Y0; gprops.endPoint.x = b->X1; gprops.endPoint.y = b->Y1; stops = mkstops(b, rt); hr = ID2D1RenderTarget_CreateLinearGradientBrush(rt, &gprops, props, stops, &brush); if (hr != S_OK) logHRESULT("error creating gradient brush in makeLinearBrush()", hr); // the example at https://msdn.microsoft.com/en-us/library/windows/desktop/dd756682%28v=vs.85%29.aspx says this is safe to do now ID2D1GradientStopCollection_Release(stops); return (ID2D1Brush *) brush; }
static void drawPreview(struct colorDialog *c, ID2D1RenderTarget *rt) { D2D1_SIZE_F size; D2D1_RECT_F rect; double r, g, b; D2D1_COLOR_F color; D2D1_BRUSH_PROPERTIES bprop; ID2D1SolidColorBrush *brush; HRESULT hr; size = realGetSize(rt); rect.left = 0; rect.top = 0; rect.right = size.width; rect.bottom = size.height; drawGrid(rt, &rect); hsv2RGB(c->h, c->s, c->v, &r, &g, &b); color.r = r; color.g = g; color.b = b; color.a = c->a; ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); bprop.opacity = 1.0; bprop.transform._11 = 1; bprop.transform._22 = 1; hr = rt->CreateSolidColorBrush(&color, &bprop, &brush); if (hr != S_OK) logHRESULT(L"error creating brush for preview", hr); rt->FillRectangle(&rect, brush); brush->Release(); }
static ID2D1GradientStopCollection *mkstops(uiDrawBrush *b, ID2D1RenderTarget *rt) { ID2D1GradientStopCollection *s; D2D1_GRADIENT_STOP *stops; size_t i; HRESULT hr; stops = uiAlloc(b->NumStops * sizeof (D2D1_GRADIENT_STOP), "D2D1_GRADIENT_STOP[]"); for (i = 0; i < b->NumStops; i++) { stops[i].position = b->Stops[i].Pos; stops[i].color.r = b->Stops[i].R; stops[i].color.g = b->Stops[i].G; stops[i].color.b = b->Stops[i].B; stops[i].color.a = b->Stops[i].A; } // TODO BUG IN MINGW // the Microsoft headers give this all 6 parameters // the MinGW headers use the 4-parameter version hr = (*(rt->lpVtbl->CreateGradientStopCollection))(rt, stops, b->NumStops, D2D1_GAMMA_2_2, // this is the default for the C++-only overload of ID2D1RenderTarget::GradientStopCollection() D2D1_EXTEND_MODE_CLAMP, &s); if (hr != S_OK) logHRESULT("error creating stop collection in mkstops()", hr); uiFree(stops); return s; }
static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) { uiTableValue *value; int checked, enabled; HTHEME theme; if (hr != S_OK) return hr; if (s->p->checkboxModelColumn == -1) return S_OK; value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxModelColumn); checked = uiTableValueInt(value); uiFreeTableValue(value); enabled = uiprivTableModelCellEditable(s->model, s->iItem, s->p->checkboxEditableModelColumn); theme = OpenThemeData(s->t->hwnd, L"button"); if (theme != NULL) { hr = drawThemedCheckbox(s, theme, checked, enabled); if (hr != S_OK) return hr; hr = CloseThemeData(theme); if (hr != S_OK) { logHRESULT(L"CloseThemeData()", hr); return hr; } } else { hr = drawUnthemedCheckbox(s, checked, enabled); if (hr != S_OK) return hr; } return S_OK; }
// TODO for this, stroke, and fill, make sure that an open path causes error state void uiDrawClip(uiDrawContext *c, uiDrawPath *path) { ID2D1PathGeometry *newPath; ID2D1GeometrySink *newSink; HRESULT hr; // if there's no current clip, borrow the path if (c->currentClip == NULL) { c->currentClip = path->path; // we have to take our own reference to that clip ID2D1PathGeometry_AddRef(c->currentClip); return; } // otherwise we have to intersect the current path with the new one // we do that into a new path, and then replace c->currentClip with that new path hr = ID2D1Factory_CreatePathGeometry(d2dfactory, &newPath); if (hr != S_OK) logHRESULT("error creating new path in uiDrawClip()", hr); hr = ID2D1PathGeometry_Open(newPath, &newSink); if (hr != S_OK) logHRESULT("error opening new path in uiDrawClip()", hr); // TODO BUG IN MINGW // the macro is supposed to take 6 parameters hr = (*(c->currentClip->lpVtbl->Base.CombineWithGeometry))( (ID2D1Geometry *) (c->currentClip), (ID2D1Geometry *) (path->path), D2D1_COMBINE_MODE_INTERSECT, NULL, // TODO is this correct or can this be set per target? D2D1_DEFAULT_FLATTENING_TOLERANCE, (ID2D1SimplifiedGeometrySink *) newSink); if (hr != S_OK) logHRESULT("error intersecting old path with new path in uiDrawClip()", hr); hr = ID2D1GeometrySink_Close(newSink); if (hr != S_OK) logHRESULT("error closing new path in uiDrawClip()", hr); ID2D1GeometrySink_Release(newSink); // okay we have the new clip; we just need to replace the old one with it ID2D1PathGeometry_Release(c->currentClip); c->currentClip = newPath; // we have a reference already; no need for another }
static IDWriteFontFamily *tryFindFamily(IDWriteFontCollection *fc, const WCHAR *name) { UINT32 index; BOOL exists; IDWriteFontFamily *family; HRESULT hr; hr = fc->FindFamilyName(name, &index, &exists); if (hr != S_OK) logHRESULT(L"error finding font family for font dialog", hr); if (!exists) return NULL; hr = fc->GetFontFamily(index, &family); if (hr != S_OK) logHRESULT(L"error extracting found font family for font dialog", hr); return family; }
void loadInitialFontDialogParams(struct fontDialogParams *params) { struct fontCollection *fc; IDWriteFontFamily *family; IDWriteFont *font; HRESULT hr; // Our preferred font is Arial 10 Regular. // 10 comes from the official font dialog. // Arial Regular is a reasonable, if arbitrary, default; it's similar to the defaults on other systems. // If Arial isn't found, we'll use Helvetica and then MS Sans Serif as fallbacks, and if not, we'll just grab the first font family in the collection. // We need the correct localized name for Regular (and possibly Arial too? let's say yes to be safe), so let's grab the strings from DirectWrite instead of hardcoding them. fc = loadFontCollection(); family = tryFindFamily(fc->fonts, L"Arial"); if (family == NULL) { family = tryFindFamily(fc->fonts, L"Helvetica"); if (family == NULL) { family = tryFindFamily(fc->fonts, L"MS Sans Serif"); if (family == NULL) { hr = fc->fonts->GetFontFamily(0, &family); if (hr != S_OK) logHRESULT(L"error getting first font out of font collection (worst case scenario)", hr); } } } // next part is simple: just get the closest match to regular hr = family->GetFirstMatchingFont( DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, &font); if (hr != S_OK) logHRESULT(L"error getting Regular font from Arial", hr); params->font = font; params->size = 10; params->familyName = fontCollectionFamilyName(fc, family); params->styleName = fontStyleName(fc, font); // don't release font; we still need it family->Release(); fontCollectionFree(fc); }
IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf) { IDWriteTypography *dt; HRESULT hr; hr = dwfactory->CreateTypography(&dt); if (hr != S_OK) logHRESULT(L"error creating IDWriteTypography", hr); uiOpenTypeFeaturesForEach(otf, addToTypography, dt); return dt; }
WCHAR *uiprivFontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) { IDWriteLocalizedStrings *names; WCHAR *str; HRESULT hr; hr = family->GetFamilyNames(&names); if (hr != S_OK) logHRESULT(L"error getting names of font out", hr); str = uiprivFontCollectionCorrectString(fc, names); names->Release(); return str; }
fontCollection *uiprivLoadFontCollection(void) { fontCollection *fc; HRESULT hr; fc = uiprivNew(fontCollection); // always get the latest available font information hr = dwfactory->GetSystemFontCollection(&(fc->fonts), TRUE); if (hr != S_OK) logHRESULT(L"error getting system font collection", hr); fc->userLocaleSuccess = GetUserDefaultLocaleName(fc->userLocale, LOCALE_NAME_MAX_LENGTH); return fc; }
static WCHAR *fontStyleName(struct fontCollection *fc, IDWriteFont *font) { IDWriteLocalizedStrings *str; WCHAR *wstr; HRESULT hr; hr = font->GetFaceNames(&str); if (hr != S_OK) logHRESULT(L"error getting font style name for font dialog", hr); wstr = fontCollectionCorrectString(fc, str); str->Release(); return wstr; }
size_t *graphemes(WCHAR *msg) { size_t len; SCRIPT_ITEM *items; int i, n; size_t *out; size_t *op; SCRIPT_LOGATTR *logattr; int j, nn; HRESULT hr; len = wcslen(msg); hr = itemize(msg, len, &items, &n); if (hr != S_OK) logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); // should be enough; 2 more just to be safe out = (size_t *) uiAlloc((len + 2) * sizeof (size_t), "size_t[]"); op = out; // note that there are actually n + 1 elements in items for (i = 0; i < n; i++) { nn = items[i + 1].iCharPos - items[i].iCharPos; logattr = new SCRIPT_LOGATTR[nn]; hr = ScriptBreak(msg + items[i].iCharPos, nn, &(items[i].a), logattr); if (hr != S_OK) logHRESULT(L"error breaking string for finding grapheme cluster boundaries", hr); for (j = 0; j < nn; j++) if (logattr[j].fCharStop != 0) *op++ = items[i].iCharPos + j; delete[] logattr; } // and handle the last item for the end of the string *op++ = items[i].iCharPos; delete[] items; return out; }
char *commonItemDialog(HWND parent, REFCLSID clsid, REFIID iid, FILEOPENDIALOGOPTIONS optsadd) { IFileDialog *d = NULL; FILEOPENDIALOGOPTIONS opts; IShellItem *result = NULL; WCHAR *wname = NULL; char *name = NULL; HRESULT hr; hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, (LPVOID *) (&d)); if (hr != S_OK) { logHRESULT(L"error creating common item dialog", hr); // always return NULL on error goto out; } hr = d->GetOptions(&opts); if (hr != S_OK) { logHRESULT(L"error getting current options", hr); goto out; } opts |= optsadd; // the other platforms don't check read-only; we won't either opts &= ~FOS_NOREADONLYRETURN; hr = d->SetOptions(opts); if (hr != S_OK) { logHRESULT(L"error setting options", hr); goto out; } hr = d->Show(parent); if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) // cancelled; return NULL like we have ready goto out; if (hr != S_OK) { logHRESULT(L"error showing dialog", hr); goto out; } hr = d->GetResult(&result); if (hr != S_OK) { logHRESULT(L"error getting dialog result", hr); goto out; } hr = result->GetDisplayName(SIGDN_FILESYSPATH, &wname); if (hr != S_OK) { logHRESULT(L"error getting filename", hr); goto out; } name = toUTF8(wname); out: if (wname != NULL) CoTaskMemFree(wname); if (result != NULL) result->Release(); if (d != NULL) d->Release(); return name; }
static uiForEach addToTypography(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) { IDWriteTypography *dt = (IDWriteTypography *) data; DWRITE_FONT_FEATURE dff; HRESULT hr; ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); // yes, the cast here is necessary (the compiler will complain otherwise)... dff.nameTag = (DWRITE_FONT_FEATURE_TAG) DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d); dff.parameter = (UINT32) value; hr = dt->AddFontFeature(dff); if (hr != S_OK) logHRESULT(L"error adding OpenType feature to IDWriteTypography", hr); return uiForEachContinue; }
static HRESULT drawThemedCheckbox(struct drawState *s, HTHEME theme, int checked, int enabled) { RECT r; SIZE size; int state; HRESULT hr; hr = GetThemePartSize(theme, s->dc, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, NULL, TS_DRAW, &size); if (hr != S_OK) { logHRESULT(L"GetThemePartSize()", hr); return hr; // TODO fall back? } r = s->m->subitemIcon; r.right = r.left + size.cx; r.bottom = r.top + size.cy; centerImageRect(&r, &(s->m->subitemIcon)); if (!checked && enabled) state = CBS_UNCHECKEDNORMAL; else if (checked && enabled) state = CBS_CHECKEDNORMAL; else if (!checked && !enabled) state = CBS_UNCHECKEDDISABLED; else state = CBS_CHECKEDDISABLED; hr = DrawThemeBackground(theme, s->dc, BP_CHECKBOX, state, &r, NULL); if (hr != S_OK) { logHRESULT(L"DrawThemeBackground()", hr); return hr; } return S_OK; }
WCHAR *uiprivFontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names) { UINT32 index; BOOL exists; UINT32 length; WCHAR *wname; HRESULT hr; // this is complex, but we ignore failure conditions to allow fallbacks // 1) If the user locale name was successfully retrieved, try it // 2) If the user locale name was not successfully retrieved, or that locale's string does not exist, or an error occurred, try L"en-us", the US English locale // 3) And if that fails, assume the first one // This algorithm is straight from MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/dd368214%28v=vs.85%29.aspx // For step 2 to work, start by setting hr to S_OK and exists to FALSE. // TODO does it skip step 2 entirely if step 1 fails? rewrite it to be a more pure conversion of the MSDN code? hr = S_OK; exists = FALSE; if (fc->userLocaleSuccess != 0) hr = names->FindLocaleName(fc->userLocale, &index, &exists); if (hr != S_OK || (hr == S_OK && !exists)) hr = names->FindLocaleName(L"en-us", &index, &exists); // TODO check hr again here? or did I decide that would be redundant because COM requires output arguments to be filled regardless of return value? if (!exists) index = 0; hr = names->GetStringLength(index, &length); if (hr != S_OK) logHRESULT(L"error getting length of font name", hr); // GetStringLength() does not include the null terminator, but GetString() does wname = (WCHAR *) uiprivAlloc((length + 1) * sizeof (WCHAR), "WCHAR[]"); hr = names->GetString(index, wname, length + 1); if (hr != S_OK) logHRESULT(L"error getting font name", hr); return wname; }
static void msgbox(HWND parent, const char *title, const char *description, TASKDIALOG_COMMON_BUTTON_FLAGS buttons, PCWSTR icon) { WCHAR *wtitle, *wdescription; HRESULT hr; wtitle = toUTF16(title); wdescription = toUTF16(description); hr = TaskDialog(parent, NULL, NULL, wtitle, wdescription, buttons, icon, NULL); if (hr != S_OK) logHRESULT(L"error showing task dialog", hr); uiFree(wdescription); uiFree(wtitle); }
void uiDrawPathEnd(uiDrawPath *p) { HRESULT hr; if (p->inFigure) { ID2D1GeometrySink_EndFigure(p->sink, D2D1_FIGURE_END_OPEN); // needed for uiDrawFreePath() p->inFigure = FALSE; } hr = ID2D1GeometrySink_Close(p->sink); if (hr != S_OK) logHRESULT("error closing path in uiDrawPathEnd()", hr); ID2D1GeometrySink_Release(p->sink); p->sink = NULL; }
static ID2D1Layer *applyClip(uiDrawContext *c) { ID2D1Layer *layer; D2D1_LAYER_PARAMETERS params; HRESULT hr; // if no clip, don't do anything if (c->currentClip == NULL) return NULL; // create a layer for clipping // we have to explicitly make the layer because we're still targeting Windows 7 // TODO MINGW BUG // this macro is supposed to take three parameters hr = (*(c->rt->lpVtbl->CreateLayer))(c->rt, NULL, &layer); if (hr != S_OK) logHRESULT("error creating clip layer in applyClip()", hr); // apply it as the clip ZeroMemory(¶ms, sizeof (D2D1_LAYER_PARAMETERS)); // this is the equivalent of InfiniteRect() in d2d1helper.h params.contentBounds.left = -FLT_MAX; params.contentBounds.top = -FLT_MAX; params.contentBounds.right = FLT_MAX; params.contentBounds.bottom = FLT_MAX; params.geometricMask = (ID2D1Geometry *) (c->currentClip); // TODO is this correct? params.maskAntialiasMode = ID2D1RenderTarget_GetAntialiasMode(c->rt); // identity matrix params.maskTransform._11 = 1; params.maskTransform._22 = 1; params.opacity = 1.0; params.opacityBrush = NULL; params.layerOptions = D2D1_LAYER_OPTIONS_NONE; // TODO is this correct? if (ID2D1RenderTarget_GetTextAntialiasMode(c->rt) == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE) params.layerOptions = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; ID2D1RenderTarget_PushLayer(c->rt, ¶ms, layer); // return the layer so it can be freed later return layer; }
static void drawArrow(ID2D1RenderTarget *rt, D2D1_POINT_2F center, double hypot) { double leg; D2D1_RECT_F rect; D2D1_MATRIX_3X2_F oldtf, rotate; D2D1_COLOR_F color; D2D1_BRUSH_PROPERTIES bprop; ID2D1SolidColorBrush *brush; HRESULT hr; // to avoid needing a geometry, this will just be a rotated square // compute the length of each side; the diagonal of the square is 2 * offset to gradient // a^2 + a^2 = c^2 -> 2a^2 = c^2 // a = sqrt(c^2/2) hypot *= hypot; hypot /= 2; leg = sqrt(hypot); rect.left = center.x - leg; rect.top = center.y - leg; rect.right = center.x + leg; rect.bottom = center.y + leg; // now we need to rotate the render target 45° (either way works) about the center point rt->GetTransform(&oldtf); rotate = oldtf * D2D1::Matrix3x2F::Rotation(45, center); rt->SetTransform(&rotate); // and draw color.r = 0.0; color.g = 0.0; color.b = 0.0; color.a = 1.0; ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); bprop.opacity = 1.0; bprop.transform._11 = 1; bprop.transform._22 = 1; hr = rt->CreateSolidColorBrush(&color, &bprop, &brush); if (hr != S_OK) logHRESULT(L"error creating brush for arrow", hr); rt->FillRectangle(&rect, brush); brush->Release(); // clean up rt->SetTransform(&oldtf); }
static void msgbox(const char *title, const char *description, TASKDIALOG_COMMON_BUTTON_FLAGS buttons, PCWSTR icon) { WCHAR *wtitle, *wdescription; HWND dialogHelper; HRESULT hr; wtitle = toUTF16(title); wdescription = toUTF16(description); dialogHelper = beginDialogHelper(); hr = TaskDialog(dialogHelper, NULL, NULL, wtitle, wdescription, buttons, icon, NULL); if (hr != S_OK) logHRESULT("error showing task dialog in msgbox()", hr); endDialogHelper(dialogHelper); uiFree(wdescription); uiFree(wtitle); }
ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd) { D2D1_RENDER_TARGET_PROPERTIES props; D2D1_HWND_RENDER_TARGET_PROPERTIES hprops; HDC dc; RECT r; ID2D1HwndRenderTarget *rt; HRESULT hr; // we need a DC for the DPI // we *could* just use the screen DPI but why when we have a window handle and its DC has a DPI dc = GetDC(hwnd); if (dc == NULL) logLastError("error getting DC to find DPI in makeHWNDRenderTarget()"); ZeroMemory(&props, sizeof (D2D1_RENDER_TARGET_PROPERTIES)); props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; props.pixelFormat.format = DXGI_FORMAT_UNKNOWN; props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN; props.dpiX = GetDeviceCaps(dc, LOGPIXELSX); props.dpiY = GetDeviceCaps(dc, LOGPIXELSY); props.usage = D2D1_RENDER_TARGET_USAGE_NONE; props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT; if (ReleaseDC(hwnd, dc) == 0) logLastError("error releasing DC for finding DPI in makeHWNDRenderTarget()"); if (GetClientRect(hwnd, &r) == 0) logLastError("error getting current size of window in makeHWNDRenderTarget()"); ZeroMemory(&hprops, sizeof (D2D1_HWND_RENDER_TARGET_PROPERTIES)); hprops.hwnd = hwnd; hprops.pixelSize.width = r.right - r.left; hprops.pixelSize.height = r.bottom - r.top; hprops.presentOptions = D2D1_PRESENT_OPTIONS_NONE; hr = ID2D1Factory_CreateHwndRenderTarget(d2dfactory, &props, &hprops, &rt); if (hr != S_OK) logHRESULT("error creating area HWND render target in makeHWNDRenderTarget()", hr); return rt; }
static ID2D1Brush *makeSolidBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props) { D2D1_COLOR_F color; ID2D1SolidColorBrush *brush; HRESULT hr; color.r = b->R; color.g = b->G; color.b = b->B; color.a = b->A; hr = ID2D1RenderTarget_CreateSolidColorBrush(rt, &color, props, &brush); if (hr != S_OK) logHRESULT("error creating solid brush in makeSolidBrush()", hr); return (ID2D1Brush *) brush; }
static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) { struct fontDialog *f; UINT32 i, nFamilies; IDWriteFontFamily *family; WCHAR *wname; LRESULT pos; HWND samplePlacement; HRESULT hr; f = uiNew(struct fontDialog); f->hwnd = hwnd; f->params = (struct fontDialogParams *) lParam; f->familyCombobox = getDlgItem(f->hwnd, rcFontFamilyCombobox); f->styleCombobox = getDlgItem(f->hwnd, rcFontStyleCombobox); f->sizeCombobox = getDlgItem(f->hwnd, rcFontSizeCombobox); f->fc = loadFontCollection(); nFamilies = f->fc->fonts->GetFontFamilyCount(); for (i = 0; i < nFamilies; i++) { hr = f->fc->fonts->GetFontFamily(i, &family); if (hr != S_OK) logHRESULT(L"error getting font family", hr); wname = fontCollectionFamilyName(f->fc, family); pos = cbAddString(f->familyCombobox, wname); uiFree(wname); cbSetItemData(f->familyCombobox, (WPARAM) pos, (LPARAM) family); } for (i = 0; defaultSizes[i].text != NULL; i++) cbInsertString(f->sizeCombobox, defaultSizes[i].text, (WPARAM) i); samplePlacement = getDlgItem(f->hwnd, rcFontSamplePlacement); uiWindowsEnsureGetWindowRect(samplePlacement, &(f->sampleRect)); mapWindowRect(NULL, f->hwnd, &(f->sampleRect)); uiWindowsEnsureDestroyWindow(samplePlacement); f->sampleBox = newD2DScratch(f->hwnd, &(f->sampleRect), (HMENU) rcFontSamplePlacement, fontDialogSampleSubProc, (DWORD_PTR) f); setupInitialFontDialogState(f); return f; }
char *commonItemDialog(REFCLSID clsid, REFIID iid, FILEOPENDIALOGOPTIONS optsadd) { IFileDialog *d; FILEOPENDIALOGOPTIONS opts; HWND dialogHelper; IShellItem *result; WCHAR *wname; char *name; HRESULT hr; hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, (LPVOID *) (&d)); if (hr != S_OK) logHRESULT("error creating common item dialog in commonItemDialog()", hr); hr = IFileDialog_GetOptions(d, &opts); if (hr != S_OK) logHRESULT("error getting current options in commonItemDialog()", hr); opts |= optsadd; hr = IFileDialog_SetOptions(d, opts); if (hr != S_OK) logHRESULT("error setting options in commonItemDialog()", hr); dialogHelper = beginDialogHelper(); hr = IFileDialog_Show(d, dialogHelper); endDialogHelper(dialogHelper); if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { IFileDialog_Release(d); return NULL; } if (hr != S_OK) logHRESULT("error showing dialog in commonItemDialog()", hr); hr = IFileDialog_GetResult(d, &result); if (hr != S_OK) logHRESULT("error getting dialog result in commonItemDialog()", hr); hr = IShellItem_GetDisplayName(result, SIGDN_FILESYSPATH, &wname); if (hr != S_OK) logHRESULT("error getting filename in commonItemDialog()", hr); name = toUTF8(wname); CoTaskMemFree(wname); IShellItem_Release(result); IFileDialog_Release(d); return name; }