Merge branch 'table'

FINALLY.

Update #310
This commit is contained in:
Pietro Gagliardi 2018-08-08 09:33:27 -04:00
commit 075d5efb61
45 changed files with 5719 additions and 133 deletions

65
OLD_uitable.h Normal file
View File

@ -0,0 +1,65 @@
// 20 june 2016
// kept in a separate file for now
typedef struct uiImage uiImage;
// TODO use const void * for const correctness
_UI_EXTERN uiImage *uiNewImage(double width, double height);
_UI_EXTERN void uiFreeImage(uiImage *i);
_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride);
typedef struct uiTableModel uiTableModel;
typedef struct uiTableModelHandler uiTableModelHandler;
// TODO actually validate these
_UI_ENUM(uiTableModelColumnType) {
uiTableModelColumnString,
uiTableModelColumnImage,
uiTableModelColumnInt,
uiTableModelColumnColor,
};
// TODO validate ranges; validate types on each getter/setter call (? table columns only?)
struct uiTableModelHandler {
int (*NumColumns)(uiTableModelHandler *, uiTableModel *);
uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int);
int (*NumRows)(uiTableModelHandler *, uiTableModel *);
void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int);
void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const void *);
};
_UI_EXTERN void *uiTableModelStrdup(const char *str);
// TODO rename the strdup one to this too
_UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a);
_UI_EXTERN void *uiTableModelGiveInt(int i);
// TODO TakeString
// TODO add const
_UI_EXTERN int uiTableModelTakeInt(void *v);
_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh);
_UI_EXTERN void uiFreeTableModel(uiTableModel *m);
_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex);
_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index);
_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex);
// TODO reordering/moving
typedef struct uiTableColumn uiTableColumn;
_UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand);
// TODO images shouldn't expand...
_UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand);
_UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand);
// TODO should these have labels?
_UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand);
_UI_EXTERN void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand);
// TODO Editable?
_UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable);
_UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn);
typedef struct uiTable uiTable;
#define uiTable(this) ((uiTable *) (this))
_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name);
_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn);
// TODO getter?
_UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn);
_UI_EXTERN uiTable *uiNewTable(uiTableModel *model);

View File

@ -30,6 +30,9 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t
*Note that today's entry (Eastern Time) may be updated later today.*
* **8 August 2018**
* Finally introduced an API for loading images, `uiImage`, and a new control, `uiTable`, for displaying tabular data. These provide enough basic functionality for now, but will be improved over time. You can read the documentation for the new features as they are [here](https://github.com/andlabs/libui/blob/f47e1423cf95ad7b1001663f3381b5a819fc67b9/uitable.h). Thanks to everyone who helped get to this point, in particular @bcampbell for the initial Windows code, and to everyone else for their patience!
* **30 May 2018**
* Merged the previous Announcements and Updates section of this README into a single News section, and merged the respective archive files into a single NEWS.md file.

1
_notes/highDPI Normal file
View File

@ -0,0 +1 @@
High DPI Displays | Qt 5.5 http://doc.qt.io/qt-5/highdpi.html bottom of page(?)

15
_notes/i18n Normal file
View File

@ -0,0 +1,15 @@
https://msdn.microsoft.com/en-us/library/windows/desktop/dd319079(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dd318103(v=vs.85).aspx
https://stackoverflow.com/questions/4663855/is-there-a-repository-for-localized-common-text-in-winforms
https://stackoverflow.com/questions/2502375/find-localized-windows-strings
https://docs.microsoft.com/en-us/windows-hardware/customize/mobile/mcsf/create-a-resource-only-dll-for-localized-strings
https://msdn.microsoft.com/en-us/library/windows/desktop/ee845043(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/cc194807.aspx
https://www.codeproject.com/Articles/10542/Easily-Load-and-Format-Strings-from-the-String-Tab
https://www.codeproject.com/Tips/431045/The-inner-working-of-FindResource-and-LoadString-W
https://mihai-nita.net/2007/05/03/how-to-localize-an-rc-file/
https://www.microsoft.com/en-us/language
https://www.microsoft.com/en-us/language/Terminology
https://www.microsoft.com/en-us/language/LicenseAgreement
https://www.microsoft.com/en-us/language/Translations
http://www.ttt.org/oscarstandards/tbx/

View File

@ -7,10 +7,11 @@ list(APPEND _LIBUI_SOURCES
common/areaevents.c
common/control.c
common/debug.c
# common/drawtext.c
common/matrix.c
common/opentype.c
common/shouldquit.c
common/tablemodel.c
common/tablevalue.c
common/userbugs.c
common/utf.c
)

22
common/OLD_table.c Normal file
View File

@ -0,0 +1,22 @@
// 21 june 2016
#include "../ui.h"
#include "uipriv.h"
void *uiTableModelGiveInt(int i)
{
return (void *) ((intptr_t) i);
}
int uiTableModelTakeInt(void *v)
{
return (int) ((intptr_t) v);
}
uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn)
{
uiTableColumn *tc;
tc = uiTableAppendColumn(t, name);
uiTableColumnAppendTextPart(tc, modelColumn, 1);
return tc;
}

20
common/table.h Normal file
View File

@ -0,0 +1,20 @@
// 23 june 2018
#ifdef __cplusplus
extern "C" {
#endif
// tablemodel.c
extern uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m);
extern int uiprivTableModelNumColumns(uiTableModel *m);
extern uiTableValueType uiprivTableModelColumnType(uiTableModel *m, int column);
extern int uiprivTableModelNumRows(uiTableModel *m);
extern uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column);
extern void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value);
extern const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams;
extern int uiprivTableModelCellEditable(uiTableModel *m, int row, int column);
extern int uiprivTableModelColorIfProvided(uiTableModel *m, int row, int column, double *r, double *g, double *b, double *a);
#ifdef __cplusplus
}
#endif

79
common/tablemodel.c Normal file
View File

@ -0,0 +1,79 @@
// 23 june 2018
#include "../ui.h"
#include "uipriv.h"
#include "table.h"
int uiprivTableModelNumColumns(uiTableModel *m)
{
uiTableModelHandler *mh;
mh = uiprivTableModelHandler(m);
return (*(mh->NumColumns))(mh, m);
}
uiTableValueType uiprivTableModelColumnType(uiTableModel *m, int column)
{
uiTableModelHandler *mh;
mh = uiprivTableModelHandler(m);
return (*(mh->ColumnType))(mh, m, column);
}
int uiprivTableModelNumRows(uiTableModel *m)
{
uiTableModelHandler *mh;
mh = uiprivTableModelHandler(m);
return (*(mh->NumRows))(mh, m);
}
uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column)
{
uiTableModelHandler *mh;
mh = uiprivTableModelHandler(m);
return (*(mh->CellValue))(mh, m, row, column);
}
void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value)
{
uiTableModelHandler *mh;
mh = uiprivTableModelHandler(m);
(*(mh->SetCellValue))(mh, m, row, column, value);
}
const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams = {
.ColorModelColumn = -1,
};
int uiprivTableModelCellEditable(uiTableModel *m, int row, int column)
{
uiTableValue *value;
int editable;
switch (column) {
case uiTableModelColumnNeverEditable:
return 0;
case uiTableModelColumnAlwaysEditable:
return 1;
}
value = uiprivTableModelCellValue(m, row, column);
editable = uiTableValueInt(value);
uiFreeTableValue(value);
return editable;
}
int uiprivTableModelColorIfProvided(uiTableModel *m, int row, int column, double *r, double *g, double *b, double *a)
{
uiTableValue *value;
if (column == -1)
return 0;
value = uiprivTableModelCellValue(m, row, column);
if (value == NULL)
return 0;
uiTableValueColor(value, r, g, b, a);
uiFreeTableValue(value);
return 1;
}

106
common/tablevalue.c Normal file
View File

@ -0,0 +1,106 @@
// 3 june 2018
#include "../ui.h"
#include "uipriv.h"
#include "table.h"
struct uiTableValue {
uiTableValueType type;
union {
char *str;
uiImage *img;
int i;
struct {
double r;
double g;
double b;
double a;
} color;
} u;
};
static uiTableValue *newTableValue(uiTableValueType type)
{
uiTableValue *v;
v = uiprivNew(uiTableValue);
v->type = type;
return v;
}
void uiFreeTableValue(uiTableValue *v)
{
switch (v->type) {
case uiTableValueTypeString:
uiprivFree(v->u.str);
break;
}
uiprivFree(v);
}
uiTableValueType uiTableValueGetType(const uiTableValue *v)
{
return v->type;
}
uiTableValue *uiNewTableValueString(const char *str)
{
uiTableValue *v;
v = newTableValue(uiTableValueTypeString);
v->u.str = (char *) uiprivAlloc((strlen(str) + 1) * sizeof (char), "char[] (uiTableValue)");
strcpy(v->u.str, str);
return v;
}
const char *uiTableValueString(const uiTableValue *v)
{
return v->u.str;
}
uiTableValue *uiNewTableValueImage(uiImage *img)
{
uiTableValue *v;
v = newTableValue(uiTableValueTypeImage);
v->u.img = img;
return v;
}
uiImage *uiTableValueImage(const uiTableValue *v)
{
return v->u.img;
}
uiTableValue *uiNewTableValueInt(int i)
{
uiTableValue *v;
v = newTableValue(uiTableValueTypeInt);
v->u.i = i;
return v;
}
int uiTableValueInt(const uiTableValue *v)
{
return v->u.i;
}
uiTableValue *uiNewTableValueColor(double r, double g, double b, double a)
{
uiTableValue *v;
v = newTableValue(uiTableValueTypeColor);
v->u.color.r = r;
v->u.color.g = g;
v->u.color.b = b;
v->u.color.a = a;
return v;
}
void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a)
{
*r = v->u.color.r;
*g = v->u.color.g;
*b = v->u.color.b;
*a = v->u.color.a;
}

View File

@ -43,6 +43,8 @@ list(APPEND _LIBUI_SOURCES
darwin/spinbox.m
darwin/stddialogs.m
darwin/tab.m
darwin/table.m
darwin/tablecolumn.m
darwin/text.m
darwin/undocumented.m
darwin/util.m

39
darwin/OLD_table.m Normal file
View File

@ -0,0 +1,39 @@
// 21 june 2016
#import "uipriv_darwin.h"
// TODOs
// - header cell seems off
// - background color shows up for a line or two below selection
// - editable NSTextFields have no intrinsic width
// - is the Y position of checkbox cells correct?
@implementation tablePart
- (NSView *)mkView:(uiTableModel *)m row:(int)row
{
// if stretchy, don't hug, otherwise hug forcibly
if (self.expand)
[view setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal];
else
[view setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal];
}
@end
uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name)
{
uiTableColumn *c;
c = uiprivNew(uiTableColumn);
c->c = [[tableColumn alloc] initWithIdentifier:@""];
c->c.libui_col = c;
// via Interface Builder
[c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)];
// 10.10 adds -[NSTableColumn setTitle:]; before then we have to do this
[[c->c headerCell] setStringValue:uiprivToNSString(name)];
// TODO is this sufficient?
[[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
c->parts = [NSMutableArray new];
[t->tv addTableColumn:c->c];
return c;
}

View File

@ -18,7 +18,7 @@ uiImage *uiNewImage(double width, double height)
return i;
}
void Image(uiImage *i)
void uiFreeImage(uiImage *i)
{
NSValue *v;
@ -30,7 +30,7 @@ void Image(uiImage *i)
uiprivFree(i);
}
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride)
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride)
{
NSBitmapImageRep *repCalibrated, *repsRGB;
uint8_t *swizzled, *bp, *sp;
@ -40,11 +40,11 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in
// OS X demands that R and B are in the opposite order from what we expect
// we must swizzle :(
// LONGTERM test on a big-endian system
swizzled = (uint8_t *) uiprivAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]");
swizzled = (uint8_t *) uiprivAlloc((byteStride * pixelHeight) * sizeof (uint8_t), "uint8_t[]");
bp = (uint8_t *) pixels;
sp = swizzled;
for (y = 0; y < pixelHeight * pixelStride; y += pixelStride)
for (x = 0; x < pixelStride; x++) {
for (y = 0; y < pixelHeight; y++){
for (x = 0; x < pixelWidth; x++) {
sp[0] = bp[2];
sp[1] = bp[1];
sp[2] = bp[0];
@ -52,6 +52,9 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in
sp += 4;
bp += 4;
}
// jump over unused bytes at end of line
bp += byteStride - pixelWidth * 4;
}
pix[0] = (unsigned char *) swizzled;
repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix
@ -63,14 +66,15 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:0
bytesPerRow:pixelStride
bytesPerRow:byteStride
bitsPerPixel:32];
repsRGB = [repCalibrated bitmapImageRepByRetaggingWithColorSpace:[NSColorSpace sRGBColorSpace]];
[repCalibrated release];
[i->i addRepresentation:repsRGB];
[repsRGB setSize:i->size];
[repsRGB release];
// don't release repsRGB; it may be equivalent to repCalibrated
// do release repCalibrated though; NSImage has a ref to either it or to repsRGB
[repCalibrated release];
// we need to keep swizzled alive for NSBitmapImageRep
[i->swizzled addObject:[NSValue valueWithPointer:swizzled]];

27
darwin/table.h Normal file
View File

@ -0,0 +1,27 @@
// 3 june 2018
#import "../common/table.h"
// table.m
// TODO get rid of forward declaration
@class uiprivTableModel;
struct uiTableModel {
uiTableModelHandler *mh;
uiprivTableModel *m;
NSMutableArray *tables;
};
struct uiTable {
uiDarwinControl c;
NSScrollView *sv;
NSTableView *tv;
uiprivScrollViewData *d;
int backgroundColumn;
uiTableModel *m;
};
// tablecolumn.m
@interface uiprivTableCellView : NSTableCellView
- (void)uiprivUpdate:(NSInteger)row;
@end
@interface uiprivTableColumn : NSTableColumn
- (uiprivTableCellView *)uiprivMakeCellView;
@end

218
darwin/table.m Normal file
View File

@ -0,0 +1,218 @@
// 3 june 2018
#import "uipriv_darwin.h"
#import "table.h"
// TODO is the initial scroll position still wrong?
@interface uiprivTableModel : NSObject<NSTableViewDataSource, NSTableViewDelegate> {
uiTableModel *m;
}
- (id)initWithModel:(uiTableModel *)model;
@end
// TODO we really need to clean up the sharing of the table and model variables...
@interface uiprivTableView : NSTableView {
uiTable *uiprivT;
uiTableModel *uiprivM;
}
- (id)initWithFrame:(NSRect)r uiprivT:(uiTable *)t uiprivM:(uiTableModel *)m;
@end
@implementation uiprivTableView
- (id)initWithFrame:(NSRect)r uiprivT:(uiTable *)t uiprivM:(uiTableModel *)m
{
self = [super initWithFrame:r];
if (self) {
self->uiprivT = t;
self->uiprivM = m;
}
return self;
}
// TODO is this correct for overflow scrolling?
static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger row)
{
NSColor *color;
double r, g, b, a;
if (t->uiprivT->backgroundColumn == -1)
return; // let Cocoa do its default thing
if (uiprivTableModelColorIfProvided(t->uiprivM, row, t->uiprivT->backgroundColumn, &r, &g, &b, &a))
color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
else {
NSArray *colors;
NSInteger index;
// this usage is primarily a guess; hopefully it is correct for the non-two color case... (TODO)
// it does seem to be correct for the two-color case, judging from comparing against the value of backgroundColor before changing it (and no, nil does not work; it just sets to white)
colors = [NSColor controlAlternatingRowBackgroundColors];
index = row % [colors count];
color = (NSColor *) [colors objectAtIndex:index];
}
[rv setBackgroundColor:color];
// color is autoreleased in all cases
}
@end
@implementation uiprivTableModel
- (id)initWithModel:(uiTableModel *)model
{
self = [super init];
if (self)
self->m = model;
return self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
return uiprivTableModelNumRows(self->m);
}
- (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row
{
uiprivTableColumn *c = (uiprivTableColumn *) cc;
uiprivTableCellView *cv;
cv = (uiprivTableCellView *) [tv makeViewWithIdentifier:[c identifier] owner:self];
if (cv == nil)
cv = [c uiprivMakeCellView];
[cv uiprivUpdate:row];
return cv;
}
- (void)tableView:(NSTableView *)tv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row
{
setBackgroundColor((uiprivTableView *) tv, rv, row);
}
@end
uiTableModel *uiNewTableModel(uiTableModelHandler *mh)
{
uiTableModel *m;
m = uiprivNew(uiTableModel);
m->mh = mh;
m->m = [[uiprivTableModel alloc] initWithModel:m];
m->tables = [NSMutableArray new];
return m;
}
void uiFreeTableModel(uiTableModel *m)
{
if ([m->tables count] != 0)
uiprivUserBug("You cannot free a uiTableModel while uiTables are using it.");
[m->tables release];
[m->m release];
uiprivFree(m);
}
void uiTableModelRowInserted(uiTableModel *m, int newIndex)
{
NSTableView *tv;
NSIndexSet *set;
set = [NSIndexSet indexSetWithIndex:newIndex];
for (tv in m->tables)
[tv insertRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone];
// set is autoreleased
}
void uiTableModelRowChanged(uiTableModel *m, int index)
{
uiprivTableView *tv;
NSTableRowView *rv;
NSUInteger i, n;
uiprivTableCellView *cv;
for (tv in m->tables) {
rv = [tv rowViewAtRow:index makeIfNecessary:NO];
if (rv != nil)
setBackgroundColor(tv, rv, index);
n = [[tv tableColumns] count];
for (i = 0; i < n; i++) {
cv = (uiprivTableCellView *) [tv viewAtColumn:i row:index makeIfNecessary:NO];
if (cv != nil)
[cv uiprivUpdate:index];
}
}
}
void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
{
NSTableView *tv;
NSIndexSet *set;
set = [NSIndexSet indexSetWithIndex:oldIndex];
for (tv in m->tables)
[tv removeRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone];
// set is autoreleased
}
uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m)
{
return m->mh;
}
uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv)
static void uiTableDestroy(uiControl *c)
{
uiTable *t = uiTable(c);
[t->m->tables removeObject:t->tv];
uiprivScrollViewFreeData(t->sv, t->d);
[t->tv release];
[t->sv release];
uiFreeControl(uiControl(t));
}
uiTable *uiNewTable(uiTableParams *p)
{
uiTable *t;
uiprivScrollViewCreateParams sp;
uiDarwinNewControl(uiTable, t);
t->m = p->Model;
t->backgroundColumn = p->RowBackgroundColorModelColumn;
t->tv = [[uiprivTableView alloc] initWithFrame:NSZeroRect uiprivT:t uiprivM:t->m];
[t->tv setDataSource:t->m->m];
[t->tv setDelegate:t->m->m];
[t->tv reloadData];
[t->m->tables addObject:t->tv];
// TODO is this sufficient?
[t->tv setAllowsColumnReordering:NO];
[t->tv setAllowsColumnResizing:YES];
[t->tv setAllowsMultipleSelection:NO];
[t->tv setAllowsEmptySelection:YES];
[t->tv setAllowsColumnSelection:NO];
[t->tv setUsesAlternatingRowBackgroundColors:YES];
[t->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular];
[t->tv setGridStyleMask:NSTableViewGridNone];
[t->tv setAllowsTypeSelect:YES];
// TODO floatsGroupRows do we even allow group rows?
memset(&sp, 0, sizeof (uiprivScrollViewCreateParams));
sp.DocumentView = t->tv;
// this is what Interface Builder sets it to
// TODO verify
sp.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];
sp.DrawsBackground = YES;
sp.Bordered = YES;
sp.HScroll = YES;
sp.VScroll = YES;
t->sv = uiprivMkScrollView(&sp, &(t->d));
// TODO WHY DOES THIS REMOVE ALL GRAPHICAL GLITCHES?
// I got the idea from http://jwilling.com/blog/optimized-nstableview-scrolling/ but that was on an unrelated problem I didn't seem to have (although I have small-ish tables to start with)
// I don't get layer-backing... am I supposed to layer-back EVERYTHING manually? I need to check Interface Builder again...
[t->sv setWantsLayer:YES];
return t;
}

720
darwin/tablecolumn.m Normal file
View File

@ -0,0 +1,720 @@
// 3 june 2018
#import "uipriv_darwin.h"
#import "table.h"
// values from interface builder
#define textColumnLeading 2
#define textColumnTrailing 2
#define imageColumnLeading 3
#define imageTextColumnLeading 7
#define checkboxTextColumnLeading 0
// these aren't provided by IB; let's just choose one
#define checkboxColumnLeading imageColumnLeading
#define progressBarColumnLeading imageColumnLeading
#define progressBarColumnTrailing progressBarColumnLeading
#define buttonColumnLeading imageColumnLeading
#define buttonColumnTrailing buttonColumnLeading
@implementation uiprivTableCellView
- (void)uiprivUpdate:(NSInteger)row
{
[self doesNotRecognizeSelector:_cmd];
}
@end
@implementation uiprivTableColumn
- (uiprivTableCellView *)uiprivMakeCellView
{
[self doesNotRecognizeSelector:_cmd];
return nil; // appease compiler
}
@end
struct textColumnCreateParams {
uiTable *t;
uiTableModel *m;
BOOL makeTextField;
int textModelColumn;
int textEditableModelColumn;
uiTableTextColumnOptionalParams textParams;
BOOL makeImageView;
int imageModelColumn;
BOOL makeCheckbox;
int checkboxModelColumn;
int checkboxEditableModelColumn;
};
@interface uiprivTextImageCheckboxTableCellView : uiprivTableCellView {
uiTable *t;
uiTableModel *m;
NSTextField *tf;
int textModelColumn;
int textEditableModelColumn;
uiTableTextColumnOptionalParams textParams;
NSImageView *iv;
int imageModelColumn;
NSButton *cb;
int checkboxModelColumn;
int checkboxEditableModelColumn;
}
- (id)initWithFrame:(NSRect)r params:(struct textColumnCreateParams *)p;
- (IBAction)uiprivOnTextFieldAction:(id)sender;
- (IBAction)uiprivOnCheckboxAction:(id)sender;
@end
@implementation uiprivTextImageCheckboxTableCellView
- (id)initWithFrame:(NSRect)r params:(struct textColumnCreateParams *)p
{
self = [super initWithFrame:r];
if (self) {
NSMutableArray *constraints;
self->t = p->t;
self->m = p->m;
constraints = [NSMutableArray new];
self->tf = nil;
if (p->makeTextField) {
self->textModelColumn = p->textModelColumn;
self->textEditableModelColumn = p->textEditableModelColumn;
self->textParams = p->textParams;
self->tf = uiprivNewLabel(@"");
// TODO set wrap and ellipsize modes?
[self->tf setTarget:self];
[self->tf setAction:@selector(uiprivOnTextFieldAction:)];
[self->tf setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self->tf];
// TODO for all three controls: set hugging and compression resistance properly
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeLeading,
NSLayoutRelationEqual,
self->tf, NSLayoutAttributeLeading,
1, -textColumnLeading,
@"uiTable cell text leading constraint")];
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTop,
NSLayoutRelationEqual,
self->tf, NSLayoutAttributeTop,
1, 0,
@"uiTable cell text top constraint")];
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTrailing,
NSLayoutRelationEqual,
self->tf, NSLayoutAttributeTrailing,
1, textColumnTrailing,
@"uiTable cell text trailing constraint")];
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeBottom,
NSLayoutRelationEqual,
self->tf, NSLayoutAttributeBottom,
1, 0,
@"uiTable cell text bottom constraint")];
}
self->iv = nil;
if (p->makeImageView) {
self->imageModelColumn = p->imageModelColumn;
self->iv = [[NSImageView alloc] initWithFrame:NSZeroRect];
[self->iv setImageFrameStyle:NSImageFrameNone];
[self->iv setImageAlignment:NSImageAlignCenter];
[self->iv setImageScaling:NSImageScaleProportionallyDown];
[self->iv setAnimates:NO];
[self->iv setEditable:NO];
[self->iv setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self->iv];
[constraints addObject:uiprivMkConstraint(self->iv, NSLayoutAttributeWidth,
NSLayoutRelationEqual,
self->iv, NSLayoutAttributeHeight,
1, 0,
@"uiTable image squareness constraint")];
if (self->tf != nil) {
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeLeading,
NSLayoutRelationEqual,
self->iv, NSLayoutAttributeLeading,
1, -imageColumnLeading,
@"uiTable cell image leading constraint")];
[constraints replaceObjectAtIndex:0
withObject:uiprivMkConstraint(self->iv, NSLayoutAttributeTrailing,
NSLayoutRelationEqual,
self->tf, NSLayoutAttributeLeading,
1, -imageTextColumnLeading,
@"uiTable cell image-text spacing constraint")];
} else
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeCenterX,
NSLayoutRelationEqual,
self->iv, NSLayoutAttributeCenterX,
1, 0,
@"uiTable cell image centering constraint")];
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTop,
NSLayoutRelationEqual,
self->iv, NSLayoutAttributeTop,
1, 0,
@"uiTable cell image top constraint")];
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeBottom,
NSLayoutRelationEqual,
self->iv, NSLayoutAttributeBottom,
1, 0,
@"uiTable cell image bottom constraint")];
}
self->cb = nil;
if (p->makeCheckbox) {
self->checkboxModelColumn = p->checkboxModelColumn;
self->checkboxEditableModelColumn = p->checkboxEditableModelColumn;
self->cb = [[NSButton alloc] initWithFrame:NSZeroRect];
[self->cb setTitle:@""];
[self->cb setButtonType:NSSwitchButton];
// doesn't seem to have an associated bezel style
[self->cb setBordered:NO];
[self->cb setTransparent:NO];
uiDarwinSetControlFont(self->cb, NSRegularControlSize);
[self->cb setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self->cb];
if (self->tf != nil) {
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeLeading,
NSLayoutRelationEqual,
self->cb, NSLayoutAttributeLeading,
1, -imageColumnLeading,
@"uiTable cell checkbox leading constraint")];
[constraints replaceObjectAtIndex:0
withObject:uiprivMkConstraint(self->cb, NSLayoutAttributeTrailing,
NSLayoutRelationEqual,
self->tf, NSLayoutAttributeLeading,
1, -imageTextColumnLeading,
@"uiTable cell checkbox-text spacing constraint")];
} else
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeCenterX,
NSLayoutRelationEqual,
self->cb, NSLayoutAttributeCenterX,
1, 0,
@"uiTable cell checkbox centering constraint")];
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTop,
NSLayoutRelationEqual,
self->cb, NSLayoutAttributeTop,
1, 0,
@"uiTable cell checkbox top constraint")];
[constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeBottom,
NSLayoutRelationEqual,
self->cb, NSLayoutAttributeBottom,
1, 0,
@"uiTable cell checkbox bottom constraint")];
}
[self addConstraints:constraints];
// take advantage of NSTableCellView-provided accessibility features
if (self->tf != nil)
[self setTextField:self->tf];
if (self->iv != nil)
[self setImageView:self->iv];
}
return self;
}
- (void)dealloc
{
if (self->cb != nil) {
[self->cb release];
self->cb = nil;
}
if (self->iv != nil) {
[self->iv release];
self->iv = nil;
}
if (self->tf != nil) {
[self->tf release];
self->tf = nil;
}
[super dealloc];
}
- (void)uiprivUpdate:(NSInteger)row
{
uiTableValue *value;
if (self->tf != nil) {
NSString *str;
NSColor *color;
double r, g, b, a;
value = uiprivTableModelCellValue(self->m, row, self->textModelColumn);
str = uiprivToNSString(uiTableValueString(value));
uiFreeTableValue(value);
[self->tf setStringValue:str];
[self->tf setEditable:uiprivTableModelCellEditable(self->m, row, self->textEditableModelColumn)];
color = [NSColor controlTextColor];
if (uiprivTableModelColorIfProvided(self->m, row, self->textParams.ColorModelColumn, &r, &g, &b, &a))
color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
[self->tf setTextColor:color];
// we don't own color in ether case; don't release
}
if (self->iv != nil) {
uiImage *img;
value = uiprivTableModelCellValue(self->m, row, self->imageModelColumn);
img = uiTableValueImage(value);
uiFreeTableValue(value);
[self->iv setImage:uiprivImageNSImage(img)];
}
if (self->cb != nil) {
value = uiprivTableModelCellValue(self->m, row, self->checkboxModelColumn);
if (uiTableValueInt(value) != 0)
[self->cb setState:NSOnState];
else
[self->cb setState:NSOffState];
uiFreeTableValue(value);
[self->cb setEnabled:uiprivTableModelCellEditable(self->m, row, self->checkboxEditableModelColumn)];
}
}
- (IBAction)uiprivOnTextFieldAction:(id)sender
{
NSInteger row;
uiTableValue *value;
row = [self->t->tv rowForView:self->tf];
value = uiNewTableValueString([[self->tf stringValue] UTF8String]);
uiprivTableModelSetCellValue(self->m, row, self->textModelColumn, value);
uiFreeTableValue(value);
// always refresh the value in case the model rejected it
// TODO document that we do this, but not for the whole row (or decide to do both, or do neither...)
[self uiprivUpdate:row];
}
- (IBAction)uiprivOnCheckboxAction:(id)sender
{
NSInteger row;
uiTableValue *value;
row = [self->t->tv rowForView:self->cb];
value = uiNewTableValueInt([self->cb state] != NSOffState);
uiprivTableModelSetCellValue(self->m, row, self->checkboxModelColumn, value);
uiFreeTableValue(value);
// always refresh the value in case the model rejected it
[self uiprivUpdate:row];
}
@end
@interface uiprivTextImageCheckboxTableColumn : uiprivTableColumn {
struct textColumnCreateParams params;
}
- (id)initWithIdentifier:(NSString *)ident params:(struct textColumnCreateParams *)p;
@end
@implementation uiprivTextImageCheckboxTableColumn
- (id)initWithIdentifier:(NSString *)ident params:(struct textColumnCreateParams *)p
{
self = [super initWithIdentifier:ident];
if (self)
self->params = *p;
return self;
}
- (uiprivTableCellView *)uiprivMakeCellView
{
uiprivTableCellView *cv;
cv = [[uiprivTextImageCheckboxTableCellView alloc] initWithFrame:NSZeroRect params:&(self->params)];
[cv setIdentifier:[self identifier]];
return cv;
}
@end
@interface uiprivProgressBarTableCellView : uiprivTableCellView {
uiTable *t;
uiTableModel *m;
NSProgressIndicator *p;
int modelColumn;
}
- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc;
@end
@implementation uiprivProgressBarTableCellView
- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc
{
self = [super initWithFrame:r];
if (self) {
self->t = table;
self->m = model;
self->modelColumn = mc;
self->p = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect];
[self->p setControlSize:NSRegularControlSize];
[self->p setBezeled:YES];
[self->p setStyle:NSProgressIndicatorBarStyle];
[self->p setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self->p];
// TODO set hugging and compression resistance properly
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeLeading,
NSLayoutRelationEqual,
self->p, NSLayoutAttributeLeading,
1, -progressBarColumnLeading,
@"uiTable cell progressbar leading constraint")];
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTop,
NSLayoutRelationEqual,
self->p, NSLayoutAttributeTop,
1, 0,
@"uiTable cell progressbar top constraint")];
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTrailing,
NSLayoutRelationEqual,
self->p, NSLayoutAttributeTrailing,
1, progressBarColumnTrailing,
@"uiTable cell progressbar trailing constraint")];
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeBottom,
NSLayoutRelationEqual,
self->p, NSLayoutAttributeBottom,
1, 0,
@"uiTable cell progressbar bottom constraint")];
}
return self;
}
- (void)dealloc
{
[self->p release];
self->p = nil;
[super dealloc];
}
- (void)uiprivUpdate:(NSInteger)row
{
uiTableValue *value;
int progress;
value = uiprivTableModelCellValue(self->m, row, self->modelColumn);
progress = uiTableValueInt(value);
uiFreeTableValue(value);
if (progress == -1) {
[self->p setIndeterminate:YES];
[self->p startAnimation:self->p];
} else if (progress == 100) {
[self->p setIndeterminate:NO];
[self->p setMaxValue:101];
[self->p setDoubleValue:101];
[self->p setDoubleValue:100];
[self->p setMaxValue:100];
} else {
[self->p setIndeterminate:NO];
[self->p setDoubleValue:(progress + 1)];
[self->p setDoubleValue:progress];
}
}
@end
@interface uiprivProgressBarTableColumn : uiprivTableColumn {
uiTable *t;
// TODO remove the need for this given t (or make t not require m, one of the two)
uiTableModel *m;
int modelColumn;
}
- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc;
@end
@implementation uiprivProgressBarTableColumn
- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc
{
self = [super initWithIdentifier:ident];
if (self) {
self->t = table;
self->m = model;
self->modelColumn = mc;
}
return self;
}
- (uiprivTableCellView *)uiprivMakeCellView
{
uiprivTableCellView *cv;
cv = [[uiprivProgressBarTableCellView alloc] initWithFrame:NSZeroRect table:self->t model:self->m modelColumn:self->modelColumn];
[cv setIdentifier:[self identifier]];
return cv;
}
@end
@interface uiprivButtonTableCellView : uiprivTableCellView {
uiTable *t;
uiTableModel *m;
NSButton *b;
int modelColumn;
int editableColumn;
}
- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec;
- (IBAction)uiprivOnClicked:(id)sender;
@end
@implementation uiprivButtonTableCellView
- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec
{
self = [super initWithFrame:r];
if (self) {
self->t = table;
self->m = model;
self->modelColumn = mc;
self->editableColumn = ec;
self->b = [[NSButton alloc] initWithFrame:NSZeroRect];
[self->b setButtonType:NSMomentaryPushInButton];
[self->b setBordered:YES];
[self->b setBezelStyle:NSRoundRectBezelStyle];
uiDarwinSetControlFont(self->b, NSRegularControlSize);
[self->b setTarget:self];
[self->b setAction:@selector(uiprivOnClicked:)];
[self->b setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self->b];
// TODO set hugging and compression resistance properly
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeLeading,
NSLayoutRelationEqual,
self->b, NSLayoutAttributeLeading,
1, -buttonColumnLeading,
@"uiTable cell button leading constraint")];
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTop,
NSLayoutRelationEqual,
self->b, NSLayoutAttributeTop,
1, 0,
@"uiTable cell button top constraint")];
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTrailing,
NSLayoutRelationEqual,
self->b, NSLayoutAttributeTrailing,
1, buttonColumnTrailing,
@"uiTable cell button trailing constraint")];
[self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeBottom,
NSLayoutRelationEqual,
self->b, NSLayoutAttributeBottom,
1, 0,
@"uiTable cell button bottom constraint")];
}
return self;
}
- (void)dealloc
{
[self->b release];
self->b = nil;
[super dealloc];
}
- (void)uiprivUpdate:(NSInteger)row
{
uiTableValue *value;
NSString *str;
value = uiprivTableModelCellValue(self->m, row, self->modelColumn);
str = uiprivToNSString(uiTableValueString(value));
uiFreeTableValue(value);
[self->b setTitle:str];
[self->b setEnabled:uiprivTableModelCellEditable(self->m, row, self->editableColumn)];
}
- (IBAction)uiprivOnClicked:(id)sender
{
NSInteger row;
row = [self->t->tv rowForView:self->b];
uiprivTableModelSetCellValue(self->m, row, self->modelColumn, NULL);
// TODO document we DON'T update the cell after doing this
// TODO or decide what to do instead
}
@end
@interface uiprivButtonTableColumn : uiprivTableColumn {
uiTable *t;
uiTableModel *m;
int modelColumn;
int editableColumn;
}
- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec;
@end
@implementation uiprivButtonTableColumn
- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec
{
self = [super initWithIdentifier:ident];
if (self) {
self->t = table;
self->m = model;
self->modelColumn = mc;
self->editableColumn = ec;
}
return self;
}
- (uiprivTableCellView *)uiprivMakeCellView
{
uiprivTableCellView *cv;
cv = [[uiprivButtonTableCellView alloc] initWithFrame:NSZeroRect table:self->t model:self->m modelColumn:self->modelColumn editableColumn:self->editableColumn];
[cv setIdentifier:[self identifier]];
return cv;
}
@end
void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
struct textColumnCreateParams p;
uiprivTableColumn *col;
NSString *str;
memset(&p, 0, sizeof (struct textColumnCreateParams));
p.t = t;
p.m = t->m;
p.makeTextField = YES;
p.textModelColumn = textModelColumn;
p.textEditableModelColumn = textEditableModelColumn;
if (textParams != NULL)
p.textParams = *textParams;
else
p.textParams = uiprivDefaultTextColumnOptionalParams;
str = [NSString stringWithUTF8String:name];
col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p];
[col setTitle:str];
[t->tv addTableColumn:col];
}
void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn)
{
struct textColumnCreateParams p;
uiprivTableColumn *col;
NSString *str;
memset(&p, 0, sizeof (struct textColumnCreateParams));
p.t = t;
p.m = t->m;
p.makeImageView = YES;
p.imageModelColumn = imageModelColumn;
str = [NSString stringWithUTF8String:name];
col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p];
[col setTitle:str];
[t->tv addTableColumn:col];
}
void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
struct textColumnCreateParams p;
uiprivTableColumn *col;
NSString *str;
memset(&p, 0, sizeof (struct textColumnCreateParams));
p.t = t;
p.m = t->m;
p.makeTextField = YES;
p.textModelColumn = textModelColumn;
p.textEditableModelColumn = textEditableModelColumn;
if (textParams != NULL)
p.textParams = *textParams;
else
p.textParams = uiprivDefaultTextColumnOptionalParams;
p.makeImageView = YES;
p.imageModelColumn = imageModelColumn;
str = [NSString stringWithUTF8String:name];
col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p];
[col setTitle:str];
[t->tv addTableColumn:col];
}
void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn)
{
struct textColumnCreateParams p;
uiprivTableColumn *col;
NSString *str;
memset(&p, 0, sizeof (struct textColumnCreateParams));
p.t = t;
p.m = t->m;
p.makeCheckbox = YES;
p.checkboxModelColumn = checkboxModelColumn;
p.checkboxEditableModelColumn = checkboxEditableModelColumn;
str = [NSString stringWithUTF8String:name];
col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p];
[col setTitle:str];
[t->tv addTableColumn:col];
}
void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
struct textColumnCreateParams p;
uiprivTableColumn *col;
NSString *str;
memset(&p, 0, sizeof (struct textColumnCreateParams));
p.t = t;
p.m = t->m;
p.makeTextField = YES;
p.textModelColumn = textModelColumn;
p.textEditableModelColumn = textEditableModelColumn;
if (textParams != NULL)
p.textParams = *textParams;
else
p.textParams = uiprivDefaultTextColumnOptionalParams;
p.makeCheckbox = YES;
p.checkboxModelColumn = checkboxModelColumn;
p.checkboxEditableModelColumn = checkboxEditableModelColumn;
str = [NSString stringWithUTF8String:name];
col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p];
[col setTitle:str];
[t->tv addTableColumn:col];
}
void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn)
{
uiprivTableColumn *col;
NSString *str;
str = [NSString stringWithUTF8String:name];
col = [[uiprivProgressBarTableColumn alloc] initWithIdentifier:str table:t model:t->m modelColumn:progressModelColumn];
[col setTitle:str];
[t->tv addTableColumn:col];
}
void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn)
{
uiprivTableColumn *col;
NSString *str;
str = [NSString stringWithUTF8String:name];
col = [[uiprivButtonTableColumn alloc] initWithIdentifier:str table:t model:t->m modelColumn:buttonModelColumn editableColumn:buttonClickableModelColumn];
[col setTitle:str];
[t->tv addTableColumn:col];
}

View File

@ -23,8 +23,6 @@
#define NSAppKitVersionNumber10_9 1265
#endif
/*TODO remove this*/typedef struct uiImage uiImage;
// map.m
typedef struct uiprivMap uiprivMap;
extern uiprivMap *uiprivNewMap(void);

View File

@ -0,0 +1,136 @@
// 25 june 2018
#include <gtk/gtk.h>
GtkWidget *mainwin;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *startProgress;
GtkWidget *startTable;
GtkWidget *progressbar;
GtkWidget *scrolledWindow;
GtkListStore *model;
GtkWidget *treeview;
GtkWidget *hbox2;
static gboolean pulseProgress(gpointer data)
{
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progressbar));
return TRUE;
}
static void onStartProgressClicked(GtkButton *button, gpointer data)
{
gtk_widget_set_sensitive(startProgress, FALSE);
g_timeout_add(100, pulseProgress, NULL);
}
gboolean pbarStarted = FALSE;
gint pbarValue;
static void pbarDataFunc(GtkTreeViewColumn *col, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data)
{
if (!pbarStarted) {
g_object_set(r,
"pulse", -1,
"value", 0,
NULL);
return;
}
pbarValue++;
if (pbarValue == G_MAXINT)
pbarValue = 1;
g_object_set(r, "pulse", pbarValue, NULL);
}
static gboolean pulseTable(gpointer data)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_indices(0, -1);
gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &iter);
gtk_tree_path_free(path);
return TRUE;
}
static void onStartTableClicked(GtkButton *button, gpointer data)
{
pbarStarted = TRUE;
pbarValue = 0;
gtk_widget_set_sensitive(startTable, FALSE);
g_timeout_add(100, pulseTable, NULL);
}
static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
{
gtk_main_quit();
return FALSE;
}
int main(void)
{
GtkTreeIter iter;
GtkTreeViewColumn *col;
GtkCellRenderer *r;
gtk_init(NULL, NULL);
mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(mainwin, "delete-event", G_CALLBACK(onClosing), NULL);
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
gtk_container_add(GTK_CONTAINER(mainwin), vbox);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_widget_set_halign(hbox, GTK_ALIGN_CENTER);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
startProgress = gtk_button_new_with_label("Start Progress Bar");
g_signal_connect(startProgress, "clicked", G_CALLBACK(onStartProgressClicked), NULL);
gtk_container_add(GTK_CONTAINER(hbox), startProgress);
startTable = gtk_button_new_with_label("Start Table Cell Renderer");
g_signal_connect(startTable, "clicked", G_CALLBACK(onStartTableClicked), NULL);
gtk_container_add(GTK_CONTAINER(hbox), startTable);
progressbar = gtk_progress_bar_new();
gtk_container_add(GTK_CONTAINER(vbox), progressbar);
scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledWindow), GTK_SHADOW_IN);
gtk_widget_set_vexpand(scrolledWindow, TRUE);
gtk_container_add(GTK_CONTAINER(vbox), scrolledWindow);
model = gtk_list_store_new(1, G_TYPE_INT);
gtk_list_store_append(model, &iter);
gtk_list_store_set(model, &iter,
0, 0,
-1);
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
gtk_container_add(GTK_CONTAINER(scrolledWindow), treeview);
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_resizable(col, TRUE);
gtk_tree_view_column_set_title(col, "Column");
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
r = gtk_cell_renderer_progress_new();
gtk_tree_view_column_pack_start(col, r, TRUE);
gtk_tree_view_column_set_cell_data_func(col, r, pbarDataFunc, NULL, NULL);
hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_widget_set_halign(hbox2, GTK_ALIGN_CENTER);
gtk_container_add(GTK_CONTAINER(vbox), hbox2);
gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("These buttons"));
gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("do nothing"));
gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("when clicked"));
gtk_widget_show_all(mainwin);
gtk_main();
return 0;
}

View File

@ -1,50 +0,0 @@
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 7f70403..e909569 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -6,7 +6,7 @@ list(APPEND _LIBUI_SOURCES
common/debug.c
common/matrix.c
common/shouldquit.c
- common/table.c
+# common/table.c
common/userbugs.c
)
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index b753a7d..a648c64 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -6,7 +6,7 @@ endif()
_add_exec(tester
drawtests.c
- images.c
+# images.c
main.c
menus.c
page1.c
@@ -27,7 +27,7 @@ _add_exec(tester
page13.c
page14.c
page15.c
- page16.c
+# page16.c
spaced.c
${_TEST_RESOURCES_RC}
)
diff --git a/test/main.c b/test/main.c
index f33f30a..18774dc 100644
--- a/test/main.c
+++ b/test/main.c
@@ -159,8 +159,8 @@ int main(int argc, char *argv[])
innerTab = newTab();
uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab));
- page16 = makePage16();
- uiTabAppend(innerTab, "Page 16", uiControl(page16));
+// page16 = makePage16();
+// uiTabAppend(innerTab, "Page 16", uiControl(page16));
if (startspaced)
setSpaced(1);

View File

@ -6,6 +6,7 @@ endif()
_add_exec(tester
drawtests.c
images.c
main.c
menus.c
page1.c
@ -26,6 +27,7 @@ _add_exec(tester
page13.c
page14.c
page15.c
page16.c
spaced.c
${_TEST_RESOURCES_RC}
)

143
test/OLD_page16.c Normal file
View File

@ -0,0 +1,143 @@
// 21 june 2016
#include "test.h"
static uiTableModelHandler mh;
static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m)
{
return 9;
}
static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column)
{
if (column == 3 || column == 4)
return uiTableModelColumnColor;
if (column == 5)
return uiTableModelColumnImage;
if (column == 7 || column == 8)
return uiTableModelColumnInt;
return uiTableModelColumnString;
}
static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m)
{
return 15;
}
static uiImage *img[2];
static char row9text[1024];
static int yellowRow = -1;
static int checkStates[15];
static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col)
{
char buf[256];
if (col == 3) {
if (row == yellowRow)
return uiTableModelGiveColor(1, 1, 0, 1);
if (row == 3)
return uiTableModelGiveColor(1, 0, 0, 1);
if (row == 11)
return uiTableModelGiveColor(0, 0.5, 1, 0.5);
return NULL;
}
if (col == 4) {
if ((row % 2) == 1)
return uiTableModelGiveColor(0.5, 0, 0.75, 1);
return NULL;
}
if (col == 5) {
if (row < 8)
return img[0];
return img[1];
}
if (col == 7)
return uiTableModelGiveInt(checkStates[row]);
if (col == 8) {
if (row == 0)
return uiTableModelGiveInt(0);
if (row == 13)
return uiTableModelGiveInt(100);
if (row == 14)
return uiTableModelGiveInt(-1);
return uiTableModelGiveInt(50);
}
switch (col) {
case 0:
sprintf(buf, "Row %d", row);
break;
case 2:
if (row == 9)
return uiTableModelStrdup(row9text);
// fall through
case 1:
strcpy(buf, "Part");
break;
case 6:
strcpy(buf, "Make Yellow");
break;
}
return uiTableModelStrdup(buf);
}
static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const void *val)
{
if (row == 9 && col == 2)
strcpy(row9text, (const char *) val);
if (col == 6)
yellowRow = row;
if (col == 7)
checkStates[row] = uiTableModelTakeInt(val);
}
uiBox *makePage16(void)
{
uiBox *page16;
uiTableModel *m;
uiTable *t;
uiTableColumn *tc;
img[0] = uiNewImage(16, 16);
appendImageNamed(img[0], "andlabs_16x16test_24june2016.png");
appendImageNamed(img[0], "andlabs_32x32test_24june2016.png");
img[1] = uiNewImage(16, 16);
appendImageNamed(img[1], "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png");
appendImageNamed(img[1], "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png");
strcpy(row9text, "Part");
memset(checkStates, 0, 15 * sizeof (int));
page16 = newVerticalBox();
mh.NumColumns = modelNumColumns;
mh.ColumnType = modelColumnType;
mh.NumRows = modelNumRows;
mh.CellValue = modelCellValue;
mh.SetCellValue = modelSetCellValue;
m = uiNewTableModel(&mh);
t = uiNewTable(m);
uiBoxAppend(page16, uiControl(t), 1);
uiTableAppendTextColumn(t, "Column 1", 0);
tc = uiTableAppendColumn(t, "Column 2");
uiTableColumnAppendImagePart(tc, 5, 0);
uiTableColumnAppendTextPart(tc, 1, 0);
uiTableColumnAppendTextPart(tc, 2, 1);
uiTableColumnPartSetTextColor(tc, 1, 4);
uiTableColumnPartSetEditable(tc, 2, 1);
uiTableSetRowBackgroundColorModelColumn(t, 3);
tc = uiTableAppendColumn(t, "Buttons");
uiTableColumnAppendCheckboxPart(tc, 7, 0);
uiTableColumnAppendButtonPart(tc, 6, 1);
tc = uiTableAppendColumn(t, "Progress Bar");
uiTableColumnAppendProgressBarPart(tc, 8, 0);
return page16;
}

202
test/images.c Normal file
View File

@ -0,0 +1,202 @@
// auto-generated by images/gen.go
#include "test.h"
static const uint32_t dat0[] = {
0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C,
0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C,
0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C,
};
static const uint32_t dat1[] = {
0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C,
0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC,
0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43,
0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43,
0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC,
0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF,
0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C,
0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C,
0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43,
0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C,
0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C,
0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C,
};
static const uint32_t dat2[] = {
0xAC676767, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0x562B2B2B, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB7B7B8, 0xFF9E9E9F, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB8B8B9, 0xFFB8B8B9, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9F9F9E, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFFB7B8B7, 0xFFC3C3C3, 0xFFC3C3C3, 0xFF9F9F9F, 0xFFEEEEEE, 0xFFEEEEEE, 0xFFC2C2C2, 0xFFEEEFEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE1E0E0, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFFB9B8B8, 0xFFC4C4C4, 0xFFC3C4C4, 0xFF9F9F9F, 0xFFEEEEEF, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFF0EFEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB9B9B8, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFC9A6A5, 0xFFAE3A36, 0xFFA50F0C, 0xFFA40201, 0xFFA40201, 0xFFA40D0A, 0xFFB03B37, 0xFF8D6969, 0x00000000, 0x00000000,
0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF9E403D, 0xFFA70A07, 0xFFC66453, 0xFFCB715C, 0xFFC9735D, 0xFFC5715B, 0xFFBF6C59, 0xFFC06E61, 0xFFAC201D, 0xC17E2927, 0x19191919,
0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA65C5B, 0xFFAF2017, 0xFFCB725D, 0xFFC7654D, 0xFFBB5338, 0xFFB65136, 0xFFB04E35, 0xFFB35D47, 0xFFAE5B45, 0xFFA95743, 0xFFAA2F28, 0xA1641716,
0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFA51412, 0xFFCA6F5A, 0xFFBF5439, 0xFFBA5237, 0xFFB44F36, 0xFFAF4D34, 0xFF886556, 0xFF765F5A, 0xFF715B56, 0xFF6B5853, 0xFF77605B, 0xF7980C0B,
0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA40201, 0xFFC76951, 0xFFB85137, 0xFFB24E36, 0xFFAD4C34, 0xFFAE653A, 0xFF95995D, 0xFF1D84A0, 0xFF177F99, 0xFF3D91A5, 0xFF3B8EA0, 0xFFA20101,
0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF9F1212, 0xFFCA7E6D, 0xFFB85F48, 0xFFAB4C33, 0xFFA64932, 0xFFB18B44, 0xFFB8BC50, 0xFF358086, 0xFF509CAD, 0xFF278397, 0xFF496E77, 0xF7920808,
0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFBC7878, 0xFF9C1D1A, 0xFFBD7E6D, 0xFFBE7F70, 0xFFBD8374, 0xFFCDC787, 0xFFCBD089, 0xFFB4B48A, 0xFF5C93A0, 0xFF44656D, 0xFF7B1618, 0xA1661D1D,
0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF934040, 0xFF9E0605, 0xFF99463D, 0xFF9E624D, 0xFFA6A14C, 0xFF9F9B4A, 0xFF948845, 0xFF4B3C43, 0xFF9A0505, 0xBA771F1F, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55351616, 0xD37F1717, 0xF99C0B0B, 0xFFA30201, 0xFFA20101, 0xF99C0B0B, 0xD3881E1E, 0x55412222, 0x00000000, 0x00000000,
};
static const uint32_t dat3[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x52303030, 0xF2919191, 0xFF999999, 0xFF9C9C9C, 0xFF9E9E9E, 0xFF9C9C9C, 0xFF999999, 0xFF969696, 0xFF939393, 0xFF8F8F8F, 0xFF8C8C8C, 0xFF888888,
0xFF848484, 0xFF818181, 0xFF7D7D7D, 0xFF7A7A7A, 0xFF767676, 0xFF727272, 0xFF6F6F6F, 0xFF6B6B6B, 0xFF686868, 0xFF646464, 0xF25F5F5F, 0x521E1E1E, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xEF8D8D8D, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFFCFCFC,
0xFFFCFCFC, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF8F8F8, 0xEF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF939393, 0xFFFDFDFD, 0xFFDCDCDC, 0xFFDDDDDD, 0xFFDEDEDE, 0xFFDEDEDE, 0xFFDFDFDF, 0xFFDFDFDF, 0xFFE0E0E0, 0xFFE1E1E1, 0xFFE1E1E1, 0xFFE1E1E1,
0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE2E2E2, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF929292, 0xFFFEFEFE, 0xFFDDDDDD, 0xFF787878, 0xFF8D8D8D, 0xFF8E8E8E, 0xFF8F8F8F, 0xFF8F8F8F, 0xFF787878, 0xFF8F8F8F, 0xFF909090, 0xFF909090,
0xFF7A7A7A, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7B7B7B, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7D7D7D, 0xFFE3E3E3, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF909090, 0xFFFEFEFE, 0xFFDEDEDE, 0xFF949494, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB1B1B1, 0xFF969696, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3,
0xFF989898, 0xFFB4B4B4, 0xFFB4B4B4, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE4E4E4, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8E8E8E, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF949494, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB2B2B2, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB4B4B4,
0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE5E5E5, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8B8B8B, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF7A7A7A, 0xFF919191, 0xFF929292, 0xFF929292, 0xFF939393, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF949494,
0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF7E7E7E, 0xFF959595, 0xFF959595, 0xFF959595, 0xFF7E7E7E, 0xFFE6E6E6, 0xFFF8F8F8, 0xFF5B5B5B, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF898989, 0xFFFDFDFD, 0xFFE0E0E0, 0xFF949494, 0xFFB0B0B0, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB2B2B2, 0xFF979797, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3,
0xFFC0C0C0, 0xFFE4E4E4, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE8E8E8, 0xFFF8F8F8, 0xFF5A5A5A, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF868686, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF7B7B7B, 0xFF929292, 0xFF939393, 0xFF949494, 0xFF949494, 0xFF7D7D7D, 0xFFBDBDBD, 0xFFBDBDBD, 0xFFBDBDBD,
0xFFA0A0A0, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFE9E9E9, 0xFFF8F8F8, 0xFF595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF838383, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF949494, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, 0xFF979797, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFE5E5E5,
0xFFC2C2C2, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFEAEAEA, 0xFFF8F8F8, 0xFF585858, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF808080, 0xFFFCFCFC, 0xFFE2E2E2, 0xFF7C7C7C, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF7E7E7E, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF,
0xFFA2A2A2, 0xFFC0C0C0, 0xFFC0C0C0, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFEBEBEB, 0xFFF8F8F8, 0xFF575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7D7D7D, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF969696, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB4B4B4, 0xFF999999, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7,
0xFFC4C4C4, 0xFFE8E8E8, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFECECEC, 0xFFF8F8F8, 0xFF555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7A7A7A, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF7F7F7F, 0xFFBFBFBF, 0xFFC0C0C0, 0xFFC1C1C1,
0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFEDEDED, 0xFFF8F8F8, 0xFF545454, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF777777, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFF999999, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9,
0xFFC4C4C4, 0xFFEAEAEA, 0xFFEAEAEA, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEEEEEE, 0xFFF8F8F8, 0xFF525252, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF737373, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF7D7D7D, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF7F7F7F, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2,
0xFFA3A2A2, 0xFFAB9191, 0xFF946060, 0xFF843D3D, 0xFF782525, 0xFF802727, 0xFF7E2B2B, 0xFF843D3D, 0xFF865252, 0xFFCCB2B2, 0xFFF6F5F5, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE6E4E4, 0xFFB18585,
0xFF7A1B1B, 0xFFA62F2F, 0xFFD35050, 0xFFF26767, 0xFFFF7070, 0xFFFE6E6E, 0xFFFC6969, 0xFFED5B5B, 0xFFCD4343, 0xFFA22525, 0xFF7E1D1D, 0xFF582C2C, 0x06020000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFB7ABAB, 0xFF7B2828, 0xFFAC3434,
0xFFF56A6A, 0xFFFF7070, 0xFFFF7070, 0xFFFE6F6F, 0xFFFC6A6A, 0xFFFA6565, 0xFFF86060, 0xFFF55C5C, 0xFFF35757, 0xFFF15252, 0xFFE64848, 0xFFA11F1F, 0xCB530000, 0x1D0C0000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF989292, 0xFF7E2626, 0xFFCB5050, 0xFFFF7474,
0xFFFF7070, 0xFFFF7070, 0xFFFD6B6B, 0xFFFA6767, 0xFFF86262, 0xFFF65D5D, 0xFFF45858, 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE93F3F, 0xFFB62424, 0xD7570000, 0x0F060000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF733D3D, 0xFFA63737, 0xFFFF7979, 0xFFFF7272,
0xFFFD6D6D, 0xFFFB6868, 0xFFF96363, 0xFFF65E5E, 0xFFF45959, 0xFFF25454, 0xFFF04F4F, 0xFFEE4A4A, 0xFFEB4545, 0xFFE94141, 0xFFE73C3C, 0xFFE53737, 0xFFE23535, 0xFF911313, 0x86360000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF6E1515, 0xFFDF6D6D, 0xFFFE7575, 0xFFFB6969,
0xFFF96464, 0xFFF75F5F, 0xFFF55A5A, 0xFFF35555, 0xFFF05050, 0xFFEE4C4C, 0xFFEC4747, 0xFFEA4242, 0xFFE73D3D, 0xFFE53838, 0xFFE33333, 0xFFE12E2E, 0xFFDF2D2D, 0xFFC22828, 0xDE590000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF670101, 0xFFED7B7B, 0xFFFA6A6A, 0xFFF86060,
0xFFF55B5B, 0xFFF35656, 0xFFF15252, 0xFFEF4D4D, 0xFFEC4848, 0xFFCD8478, 0xFF777CAE, 0xFF777BAD, 0xFF7275A7, 0xFF6B6FA1, 0xFF67699B, 0xFF606396, 0xFF5E6091, 0xFF7E5E82, 0xFC660000, 0x02000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF670000, 0xFFD86262, 0xFFF76D6D, 0xFFF45858,
0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE84341, 0xFFC5E18C, 0xFF8A939F, 0xFF5889C7, 0xFF5182C1, 0xFF4B7CBA, 0xFF4475B4, 0xFF3D6EAE, 0xFF4572AD, 0xFF6A6087, 0xFF670000, 0x0E000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF690D0D, 0xFFA32424, 0xFFEF7B7B, 0xFFF15A5A,
0xFFEE4A4A, 0xFFEB4545, 0xFFE94040, 0xFFE73C3C, 0xFFD86B4D, 0xFFADE773, 0xFFABC35F, 0xFF5585C0, 0xFF4D7EBD, 0xFF4778B7, 0xFF4071B0, 0xFF4372AE, 0xFF6B7AA6, 0xFF544267, 0xE85C0000, 0x16000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF773939, 0xFF7A0909, 0xFFBB3232, 0xFFE87171,
0xFFED6161, 0xFFE84242, 0xFFE53838, 0xFFE33333, 0xFFBA9F4E, 0xFF99E053, 0xFF8FDC43, 0xFF848A66, 0xFF497AB9, 0xFF4777B4, 0xFF5882B9, 0xFF797FA6, 0xFF455E90, 0xFF651723, 0xAC3E0000, 0x13000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF766B6B, 0xFF6D0E0E, 0xFF881111, 0xFFB02323,
0xFFCA4545, 0xFFE26666, 0xFFE85D5D, 0xFFE24F4B, 0xFFA1D656, 0xFF91DC47, 0xFF8BD93C, 0xFF90C93A, 0xFF7494BB, 0xFF848EB4, 0xFF70688E, 0xFF3B5E92, 0xFF602942, 0xEF5F0000, 0x400B0000, 0x08000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFBDA9A9, 0xFF711515, 0xFF7B0C0C,
0xFFA21D1D, 0xFFAD1B1B, 0xFFB62727, 0xFFB24F33, 0xFF93A640, 0xFF96A840, 0xFF91A73C, 0xFF869F33, 0xFF6E675B, 0xFF375C93, 0xFF544D75, 0xFF6B1823, 0xEB5C0000, 0x59130000, 0x16000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFD1C9C9, 0xFF905353,
0xFF6A0505, 0xFF770A0A, 0xFF8E1414, 0xFF91331C, 0xFF728723, 0xFF709024, 0xFF6E8F23, 0xFF737C1F, 0xFF703C31, 0xFF6C141B, 0xFF6A0505, 0xFB531616, 0x52080000, 0x1D000000, 0x01000000, 0x00000000,
0x00000000, 0x00000000, 0x0D000000, 0x22000000, 0x78191919, 0xF6535353, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF555555, 0xFF4F4F4F,
0xFF4B4343, 0xFF522828, 0xFF5A1616, 0xFF610A0A, 0xFF650404, 0xFF670000, 0xFF650404, 0xFF600909, 0xFF591515, 0xFF502626, 0xF9453C3C, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000,
0x00000000, 0x00000000, 0x06000000, 0x1D000000, 0x30000000, 0x40000000, 0x47000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000,
0x4C000000, 0x54000000, 0x5E000000, 0x65000000, 0x69000000, 0x6B000000, 0x6B000000, 0x69000000, 0x63000000, 0x52000000, 0x4B000000, 0x3B000000, 0x29000000, 0x0C000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x0F000000, 0x15000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000,
0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x17000000, 0x18000000, 0x17000000, 0x17000000, 0x16000000, 0x14000000, 0x0F000000, 0x08000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000,
};
static const struct {
const char *name;
void *data;
int width;
int height;
int stride;
} files[] = {
{ "andlabs_16x16test_24june2016.png", dat0, 16, 16, 64 },
{ "andlabs_32x32test_24june2016.png", dat1, 32, 32, 128 },
{ "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png", dat2, 16, 16, 64 },
{ "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png", dat3, 32, 32, 128 },
};
void appendImageNamed(uiImage *img, const char *name)
{
int i;
i = 0;
for (;;) {
if (strcmp(name, files[i].name) == 0) {
uiImageAppend(img, files[i].data, files[i].width, files[i].height, files[i].stride);
return;
}
i++;
}
}

View File

@ -159,8 +159,8 @@ int main(int argc, char *argv[])
innerTab = newTab();
uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab));
// page16 = makePage16();
// uiTabAppend(innerTab, "Page 16", uiControl(page16));
page16 = makePage16();
uiTabAppend(innerTab, "Page 16", uiControl(page16));
if (startspaced)
setSpaced(1);
@ -174,6 +174,7 @@ int main(int argc, char *argv[])
;
}
printf("after uiMain()\n");
freePage16();
uiUninit();
printf("after uiUninit()\n");
return 0;

163
test/page16.c Normal file
View File

@ -0,0 +1,163 @@
// 21 june 2016
#include "test.h"
static uiTableModelHandler mh;
static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m)
{
return 9;
}
static uiTableValueType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column)
{
if (column == 3 || column == 4)
return uiTableValueTypeColor;
if (column == 5)
return uiTableValueTypeImage;
if (column == 7 || column == 8)
return uiTableValueTypeInt;
return uiTableValueTypeString;
}
static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m)
{
return 15;
}
static uiImage *img[2];
static char row9text[1024];
static int yellowRow = -1;
static int checkStates[15];
static uiTableValue *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col)
{
char buf[256];
if (col == 3) {
if (row == yellowRow)
return uiNewTableValueColor(1, 1, 0, 1);
if (row == 3)
return uiNewTableValueColor(1, 0, 0, 1);
if (row == 11)
return uiNewTableValueColor(0, 0.5, 1, 0.5);
return NULL;
}
if (col == 4) {
if ((row % 2) == 1)
return uiNewTableValueColor(0.5, 0, 0.75, 1);
return NULL;
}
if (col == 5) {
if (row < 8)
return uiNewTableValueImage(img[0]);
return uiNewTableValueImage(img[1]);
}
if (col == 7)
return uiNewTableValueInt(checkStates[row]);
if (col == 8) {
if (row == 0)
return uiNewTableValueInt(0);
if (row == 13)
return uiNewTableValueInt(100);
if (row == 14)
return uiNewTableValueInt(-1);
return uiNewTableValueInt(50);
}
switch (col) {
case 0:
sprintf(buf, "Row %d", row);
break;
case 2:
if (row == 9)
return uiNewTableValueString(row9text);
// fall through
case 1:
strcpy(buf, "Part");
break;
case 6:
strcpy(buf, "Make Yellow");
break;
}
return uiNewTableValueString(buf);
}
static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const uiTableValue *val)
{
if (row == 9 && col == 2)
strcpy(row9text, uiTableValueString(val));
if (col == 6) {
int prevYellowRow;
prevYellowRow = yellowRow;
yellowRow = row;
if (prevYellowRow != -1)
uiTableModelRowChanged(m, prevYellowRow);
uiTableModelRowChanged(m, yellowRow);
}
if (col == 7)
checkStates[row] = uiTableValueInt(val);
}
static uiTableModel *m;
uiBox *makePage16(void)
{
uiBox *page16;
uiTable *t;
uiTableParams p;
uiTableTextColumnOptionalParams tp;
img[0] = uiNewImage(16, 16);
appendImageNamed(img[0], "andlabs_16x16test_24june2016.png");
appendImageNamed(img[0], "andlabs_32x32test_24june2016.png");
img[1] = uiNewImage(16, 16);
appendImageNamed(img[1], "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png");
appendImageNamed(img[1], "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png");
strcpy(row9text, "Part");
memset(checkStates, 0, 15 * sizeof (int));
page16 = newVerticalBox();
mh.NumColumns = modelNumColumns;
mh.ColumnType = modelColumnType;
mh.NumRows = modelNumRows;
mh.CellValue = modelCellValue;
mh.SetCellValue = modelSetCellValue;
m = uiNewTableModel(&mh);
memset(&p, 0, sizeof (uiTableParams));
p.Model = m;
p.RowBackgroundColorModelColumn = 3;
t = uiNewTable(&p);
uiBoxAppend(page16, uiControl(t), 1);
uiTableAppendTextColumn(t, "Column 1",
0, uiTableModelColumnNeverEditable, NULL);
memset(&tp, 0, sizeof (uiTableTextColumnOptionalParams));
tp.ColorModelColumn = 4;
uiTableAppendImageTextColumn(t, "Column 2",
5,
1, uiTableModelColumnNeverEditable, &tp);
uiTableAppendTextColumn(t, "Editable",
2, uiTableModelColumnAlwaysEditable, NULL);
uiTableAppendCheckboxColumn(t, "Checkboxes",
7, uiTableModelColumnAlwaysEditable);
uiTableAppendButtonColumn(t, "Buttons",
6, uiTableModelColumnAlwaysEditable);
uiTableAppendProgressBarColumn(t, "Progress Bar",
8);
return page16;
}
void freePage16(void)
{
uiFreeTableModel(m);
uiFreeImage(img[1]);
uiFreeImage(img[0]);
}

View File

@ -89,3 +89,10 @@ extern uiTab *makePage14(void);
// page15.c
extern uiBox *makePage15(uiWindow *);
// page16.c
extern uiBox *makePage16(void);
extern void freePage16(void);
// images.c
extern void appendImageNamed(uiImage *img, const char *name);

332
ui.h
View File

@ -512,6 +512,7 @@ _UI_EXTERN void uiDrawRestore(uiDrawContext *c);
// contents as necessary.
typedef struct uiAttribute uiAttribute;
// @role uiAttribute destructor
// uiFreeAttribute() frees a uiAttribute. You generally do not need to
// call this yourself, as uiAttributedString does this for you. In fact,
// it is an error to call this function on a uiAttribute that has been
@ -1123,6 +1124,337 @@ _UI_EXTERN int uiGridPadded(uiGrid *g);
_UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded);
_UI_EXTERN uiGrid *uiNewGrid(void);
// uiImage stores an image for display on screen.
//
// Images are built from one or more representations, each with the
// same aspect ratio but a different pixel size. libui automatically
// selects the most appropriate representation for drawing the image
// when it comes time to draw the image; what this means depends
// on the pixel density of the target context. Therefore, one can use
// uiImage to draw higher-detailed images on higher-density
// displays. The typical use cases are either:
//
// - have just a single representation, at which point all screens
// use the same image, and thus uiImage acts like a simple
// bitmap image, or
// - have two images, one at normal resolution and one at 2x
// resolution; this matches the current expectations of some
// desktop systems at the time of writing (mid-2018)
//
// uiImage is very simple: it only supports non-premultiplied 32-bit
// RGBA images, and libui does not provide any image file loading
// or image format conversion utilities on top of that.
typedef struct uiImage uiImage;
// @role uiImage constructor
// uiNewImage creates a new uiImage with the given width and
// height. This width and height should be the size in points of the
// image in the device-independent case; typically this is the 1x size.
// TODO for all uiImage functions: use const void * for const correctness
_UI_EXTERN uiImage *uiNewImage(double width, double height);
// @role uiImage destructor
// uiFreeImage frees the given image and all associated resources.
_UI_EXTERN void uiFreeImage(uiImage *i);
// uiImageAppend adds a representation to the uiImage.
// pixels should point to a byte array of non-premultiplied pixels
// stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the R of the
// first pixel and [3] is the A of the first pixel). pixelWidth and
// pixelHeight is the size *in pixels* of the image, and pixelStride is
// the number *of bytes* per row of the pixels array. Therefore,
// pixels itself must be at least byteStride * pixelHeight bytes long.
// TODO see if we either need the stride or can provide a way to get the OS-preferred stride (in cairo we do)
_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride);
// uiTableValue stores a value to be passed along uiTable and
// uiTableModel.
//
// You do not create uiTableValues directly; instead, you create a
// uiTableValue of a given type using the specialized constructor
// functions.
//
// uiTableValues are immutable and the uiTableModel and uiTable
// take ownership of the uiTableValue object once returned, copying
// its contents as necessary.
typedef struct uiTableValue uiTableValue;
// @role uiTableValue destructor
// uiFreeTableValue() frees a uiTableValue. You generally do not
// need to call this yourself, as uiTable and uiTableModel do this
// for you. In fact, it is an error to call this function on a uiTableValue
// that has been given to a uiTable or uiTableModel. You can call this,
// however, if you created a uiTableValue that you aren't going to
// use later, or if you called a uiTableModelHandler method directly
// and thus never transferred ownership of the uiTableValue.
_UI_EXTERN void uiFreeTableValue(uiTableValue *v);
// uiTableValueType holds the possible uiTableValue types that may
// be returned by uiTableValueGetType(). Refer to the documentation
// for each type's constructor function for details on each type.
// TODO actually validate these
_UI_ENUM(uiTableValueType) {
uiTableValueTypeString,
uiTableValueTypeImage,
uiTableValueTypeInt,
uiTableValueTypeColor,
};
// uiTableValueGetType() returns the type of v.
// TODO I don't like this name
_UI_EXTERN uiTableValueType uiTableValueGetType(const uiTableValue *v);
// uiNewTableValueString() returns a new uiTableValue that contains
// str. str is copied; you do not need to keep it alive after
// uiNewTableValueString() returns.
_UI_EXTERN uiTableValue *uiNewTableValueString(const char *str);
// uiTableValueString() returns the string stored in v. The returned
// string is owned by v. It is an error to call this on a uiTableValue
// that does not hold a string.
_UI_EXTERN const char *uiTableValueString(const uiTableValue *v);
// uiNewTableValueImage() returns a new uiTableValue that contains
// the given uiImage.
//
// Unlike other similar constructors, uiNewTableValueImage() does
// NOT copy the image. This is because images are comparatively
// larger than the other objects in question. Therefore, you MUST
// keep the image alive as long as the returned uiTableValue is alive.
// As a general rule, if libui calls a uiTableModelHandler method, the
// uiImage is safe to free once any of your code is once again
// executed.
_UI_EXTERN uiTableValue *uiNewTableValueImage(uiImage *img);
// uiTableValueImage() returns the uiImage stored in v. As these
// images are not owned by v, you should not assume anything
// about the lifetime of the image (unless you created the image,
// and thus control its lifetime). It is an error to call this on a
// uiTableValue that does not hold an image.
_UI_EXTERN uiImage *uiTableValueImage(const uiTableValue *v);
// uiNewTableValueInt() returns a uiTableValue that stores the given
// int. This can be used both for boolean values (nonzero is true, as
// in C) or progresses (in which case the valid range is -1..100
// inclusive).
_UI_EXTERN uiTableValue *uiNewTableValueInt(int i);
// uiTableValueInt() returns the int stored in v. It is an error to call
// this on a uiTableValue that does not store an int.
_UI_EXTERN int uiTableValueInt(const uiTableValue *v);
// uiNewTableValueColor() returns a uiTableValue that stores the
// given color.
_UI_EXTERN uiTableValue *uiNewTableValueColor(double r, double g, double b, double a);
// uiTableValueColor() returns the color stored in v. It is an error to
// call this on a uiTableValue that does not store a color.
// TODO define whether all this, for both uiTableValue and uiAttribute, is undefined behavior or a caught error
_UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a);
// uiTableModel is an object that provides the data for a uiTable.
// This data is returned via methods you provide in the
// uiTableModelHandler struct.
//
// uiTableModel represents data using a table, but this table does
// not map directly to uiTable itself. Instead, you can have data
// columns which provide instructions for how to render a given
// uiTable's column — for instance, one model column can be used
// to give certain rows of a uiTable a different background color.
// Row numbers DO match with uiTable row numbers.
//
// Once created, the number and data types of columns of a
// uiTableModel cannot change.
//
// Row and column numbers start at 0. A uiTableModel can be
// associated with more than one uiTable at a time.
typedef struct uiTableModel uiTableModel;
// uiTableModelHandler defines the methods that uiTableModel
// calls when it needs data. Once a uiTableModel is created, these
// methods cannot change.
typedef struct uiTableModelHandler uiTableModelHandler;
// TODO validate ranges; validate types on each getter/setter call (? table columns only?)
struct uiTableModelHandler {
// NumColumns returns the number of model columns in the
// uiTableModel. This value must remain constant through the
// lifetime of the uiTableModel. This method is not guaranteed
// to be called depending on the system.
// TODO strongly check column numbers and types on all platforms so these clauses can go away
int (*NumColumns)(uiTableModelHandler *, uiTableModel *);
// ColumnType returns the value type of the data stored in
// the given model column of the uiTableModel. The returned
// values must remain constant through the lifetime of the
// uiTableModel. This method is not guaranteed to be called
// depending on the system.
uiTableValueType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int);
// NumRows returns the number or rows in the uiTableModel.
// This value must be non-negative.
int (*NumRows)(uiTableModelHandler *, uiTableModel *);
// CellValue returns a uiTableValue corresponding to the model
// cell at (row, column). The type of the returned uiTableValue
// must match column's value type. Under some circumstances,
// NULL may be returned; refer to the various methods that add
// columns to uiTable for details. Once returned, the uiTable
// that calls CellValue will free the uiTableValue returned.
uiTableValue *(*CellValue)(uiTableModelHandler *mh, uiTableModel *m, int row, int column);
// SetCellValue changes the model cell value at (row, column)
// in the uiTableModel. Within this function, either do nothing
// to keep the current cell value or save the new cell value as
// appropriate. After SetCellValue is called, the uiTable will
// itself reload the table cell. Under certain conditions, the
// uiTableValue passed in can be NULL; refer to the various
// methods that add columns to uiTable for details. Once
// returned, the uiTable that called SetCellValue will free the
// uiTableValue passed in.
void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *);
};
// @role uiTableModel constructor
// uiNewTableModel() creates a new uiTableModel with the given
// handler methods.
_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh);
// @role uiTableModel destructor
// uiFreeTableModel() frees the given table model. It is an error to
// free table models currently associated with a uiTable.
_UI_EXTERN void uiFreeTableModel(uiTableModel *m);
// uiTableModelRowInserted() tells any uiTable associated with m
// that a new row has been added to m at index index. You call
// this function when the number of rows in your model has
// changed; after calling it, NumRows() should returm the new row
// count.
_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex);
// uiTableModelRowChanged() tells any uiTable associated with m
// that the data in the row at index has changed. You do not need to
// call this in your SetCellValue() handlers, but you do need to call
// this if your data changes at some other point.
_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index);
// uiTableModelRowDeleted() tells any uiTable associated with m
// that the row at index index has been deleted. You call this
// function when the number of rows in your model has changed;
// after calling it, NumRows() should returm the new row
// count.
// TODO for this and Inserted: make sure the "after" part is right; clarify if it's after returning or after calling
_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex);
// TODO reordering/moving
// uiTableModelColumnNeverEditable and
// uiTableModelColumnAlwaysEditable are the value of an editable
// model column parameter to one of the uiTable create column
// functions; if used, that jparticular uiTable colum is not editable
// by the user and always editable by the user, respectively.
#define uiTableModelColumnNeverEditable (-1)
#define uiTableModelColumnAlwaysEditable (-2)
// uiTableTextColumnOptionalParams are the optional parameters
// that control the appearance of the text column of a uiTable.
typedef struct uiTableTextColumnOptionalParams uiTableTextColumnOptionalParams;
// uiTableParams defines the parameters passed to uiNewTable().
typedef struct uiTableParams uiTableParams;
struct uiTableTextColumnOptionalParams {
// ColorModelColumn is the model column containing the
// text color of this uiTable column's text, or -1 to use the
// default color.
//
// If CellValue() for this column for any cell returns NULL, that
// cell will also use the default text color.
int ColorModelColumn;
};
struct uiTableParams {
// Model is the uiTableModel to use for this uiTable.
// This parameter cannot be NULL.
uiTableModel *Model;
// RowBackgroundColorModelColumn is a model column
// number that defines the background color used for the
// entire row in the uiTable, or -1 to use the default color for
// all rows.
//
// If CellValue() for this column for any row returns NULL, that
// row will also use the default background color.
int RowBackgroundColorModelColumn;
};
// uiTable is a uiControl that shows tabular data, allowing users to
// manipulate rows of such data at a time.
typedef struct uiTable uiTable;
#define uiTable(this) ((uiTable *) (this))
// uiTableAppendTextColumn() appends a text column to t.
// name is displayed in the table header.
// textModelColumn is where the text comes from.
// If a row is editable according to textEditableModelColumn,
// SetCellValue() is called with textModelColumn as the column.
_UI_EXTERN void uiTableAppendTextColumn(uiTable *t,
const char *name,
int textModelColumn,
int textEditableModelColumn,
uiTableTextColumnOptionalParams *textParams);
// uiTableAppendImageColumn() appends an image column to t.
// Images are drawn at icon size, appropriate to the pixel density
// of the screen showing the uiTable.
_UI_EXTERN void uiTableAppendImageColumn(uiTable *t,
const char *name,
int imageModelColumn);
// uiTableAppendImageTextColumn() appends a column to t that
// shows both an image and text.
_UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t,
const char *name,
int imageModelColumn,
int textModelColumn,
int textEditableModelColumn,
uiTableTextColumnOptionalParams *textParams);
// uiTableAppendCheckboxColumn appends a column to t that
// contains a checkbox that the user can interact with (assuming the
// checkbox is editable). SetCellValue() will be called with
// checkboxModelColumn as the column in this case.
_UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t,
const char *name,
int checkboxModelColumn,
int checkboxEditableModelColumn);
// uiTableAppendCheckboxTextColumn() appends a column to t
// that contains both a checkbox and text.
_UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t,
const char *name,
int checkboxModelColumn,
int checkboxEditableModelColumn,
int textModelColumn,
int textEditableModelColumn,
uiTableTextColumnOptionalParams *textParams);
// uiTableAppendProgressBarColumn() appends a column to t
// that displays a progress bar. These columns work like
// uiProgressBar: a cell value of 0..100 displays that percentage, and
// a cell value of -1 displays an indeterminate progress bar.
_UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t,
const char *name,
int progressModelColumn);
// uiTableAppendButtonColumn() appends a column to t
// that shows a button that the user can click on. When the user
// does click on the button, SetCellValue() is called with a NULL
// value and buttonModelColumn as the column.
// CellValue() on buttonModelColumn should return the text to show
// in the button.
_UI_EXTERN void uiTableAppendButtonColumn(uiTable *t,
const char *name,
int buttonModelColumn,
int buttonClickableModelColumn);
// uiNewTable() creates a new uiTable with the specified parameters.
_UI_EXTERN uiTable *uiNewTable(uiTableParams *params);
#ifdef __cplusplus
}
#endif

View File

@ -43,6 +43,8 @@ list(APPEND _LIBUI_SOURCES
unix/spinbox.c
unix/stddialogs.c
unix/tab.c
unix/table.c
unix/tablemodel.c
unix/text.c
unix/util.c
unix/window.c

406
unix/OLD_table.c Normal file
View File

@ -0,0 +1,406 @@
// 26 june 2016
#include "uipriv_unix.h"
void *uiTableModelStrdup(const char *str)
{
return g_strdup(str);
}
void *uiTableModelGiveColor(double r, double g, double b, double a)
{
GdkRGBA rgba;
rgba.red = r;
rgba.green = g;
rgba.blue = b;
rgba.alpha = a;
return gdk_rgba_copy(&rgba);
}
uiTableModel *uiNewTableModel(uiTableModelHandler *mh)
{
uiTableModel *m;
m = uiTableModel(g_object_new(uiTableModelType, NULL));
m->mh = mh;
return m;
}
void uiFreeTableModel(uiTableModel *m)
{
g_object_unref(m);
}
void uiTableModelRowInserted(uiTableModel *m, int newIndex)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_indices(newIndex, -1);
iter.stamp = STAMP_GOOD;
iter.user_data = GINT_TO_POINTER(newIndex);
gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter);
gtk_tree_path_free(path);
}
void uiTableModelRowChanged(uiTableModel *m, int index)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_indices(index, -1);
iter.stamp = STAMP_GOOD;
iter.user_data = GINT_TO_POINTER(index);
gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter);
gtk_tree_path_free(path);
}
void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
{
GtkTreePath *path;
path = gtk_tree_path_new_from_indices(oldIndex, -1);
gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path);
gtk_tree_path_free(path);
}
enum {
partText,
partImage,
partButton,
partCheckbox,
partProgressBar,
};
struct tablePart {
int type;
int textColumn;
int imageColumn;
int valueColumn;
int colorColumn;
GtkCellRenderer *r;
uiTable *tv; // for pixbufs and background color
};
struct uiTableColumn {
GtkTreeViewColumn *c;
uiTable *tv; // for pixbufs and background color
GPtrArray *parts;
};
struct uiTable {
uiUnixControl c;
GtkWidget *widget;
GtkContainer *scontainer;
GtkScrolledWindow *sw;
GtkWidget *treeWidget;
GtkTreeView *tv;
GPtrArray *columns;
uiTableModel *model;
int backgroundColumn;
};
// use the same size as GtkFileChooserWidget's treeview
// TODO refresh when icon theme changes
// TODO doesn't work when scaled
// TODO is this even necessary?
static void setImageSize(GtkCellRenderer *r)
{
gint size;
gint width, height;
gint xpad, ypad;
size = 16; // fallback used by GtkFileChooserWidget
if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height) != FALSE)
size = MAX(width, height);
gtk_cell_renderer_get_padding(r, &xpad, &ypad);
gtk_cell_renderer_set_fixed_size(r,
2 * xpad + size,
2 * ypad + size);
}
static void applyColor(GtkTreeModel *mm, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet)
{
GValue value = G_VALUE_INIT;
GdkRGBA *rgba;
gtk_tree_model_get_value(mm, iter, modelColumn, &value);
rgba = (GdkRGBA *) g_value_get_boxed(&value);
if (rgba != NULL)
g_object_set(r, prop, rgba, NULL);
else
g_object_set(r, propSet, FALSE, NULL);
g_value_unset(&value);
}
static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data)
{
struct tablePart *part = (struct tablePart *) data;
GValue value = G_VALUE_INIT;
const gchar *str;
uiImage *img;
int pval;
switch (part->type) {
case partText:
gtk_tree_model_get_value(mm, iter, part->textColumn, &value);
str = g_value_get_string(&value);
g_object_set(r, "text", str, NULL);
if (part->colorColumn != -1)
applyColor(mm, iter,
part->colorColumn,
r, "foreground-rgba", "foreground-set");
break;
case partImage:
//TODO setImageSize(r);
gtk_tree_model_get_value(mm, iter, part->imageColumn, &value);
img = (uiImage *) g_value_get_pointer(&value);
g_object_set(r, "surface",
uiprivImageAppropriateSurface(img, part->tv->treeWidget),
NULL);
break;
case partButton:
gtk_tree_model_get_value(mm, iter, part->textColumn, &value);
str = g_value_get_string(&value);
g_object_set(r, "text", str, NULL);
break;
case partCheckbox:
gtk_tree_model_get_value(mm, iter, part->valueColumn, &value);
g_object_set(r, "active", g_value_get_int(&value) != 0, NULL);
break;
case partProgressBar:
gtk_tree_model_get_value(mm, iter, part->valueColumn, &value);
pval = g_value_get_int(&value);
if (pval == -1) {
// TODO
} else
g_object_set(r,
"pulse", -1,
"value", pval,
NULL);
break;
}
g_value_unset(&value);
if (part->tv->backgroundColumn != -1)
applyColor(mm, iter,
part->tv->backgroundColumn,
r, "cell-background-rgba", "cell-background-set");
}
static void onEdited(struct tablePart *part, int column, const char *pathstr, const void *data)
{
GtkTreePath *path;
int row;
uiTableModel *m;
path = gtk_tree_path_new_from_string(pathstr);
row = gtk_tree_path_get_indices(path)[0];
gtk_tree_path_free(path);
m = part->tv->model;
(*(m->mh->SetCellValue))(m->mh, m, row, column, data);
// and update
uiTableModelRowChanged(m, row);
}
static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand)
{
part->r = r;
gtk_tree_view_column_pack_start(c->c, part->r, expand != 0);
gtk_tree_view_column_set_cell_data_func(c->c, part->r, dataFunc, part, NULL);
g_ptr_array_add(c->parts, part);
}
static void textEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data)
{
struct tablePart *part = (struct tablePart *) data;
onEdited(part, part->textColumn, path, newText);
}
void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand)
{
struct tablePart *part;
GtkCellRenderer *r;
part = uiprivNew(struct tablePart);
part->type = partText;
part->textColumn = modelColumn;
part->tv = c->tv;
part->colorColumn = -1;
r = gtk_cell_renderer_text_new();
g_object_set(r, "editable", FALSE, NULL);
g_signal_connect(r, "edited", G_CALLBACK(textEdited), part);
appendPart(c, part, r, expand);
}
void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand)
{
struct tablePart *part;
part = uiprivNew(struct tablePart);
part->type = partImage;
part->imageColumn = modelColumn;
part->tv = c->tv;
appendPart(c, part,
gtk_cell_renderer_pixbuf_new(),
expand);
}
// TODO wrong type here
static void buttonClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data)
{
struct tablePart *part = (struct tablePart *) data;
onEdited(part, part->textColumn, pathstr, NULL);
}
void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand)
{
struct tablePart *part;
GtkCellRenderer *r;
part = uiprivNew(struct tablePart);
part->type = partButton;
part->textColumn = modelColumn;
part->tv = c->tv;
r = uiprivNewCellRendererButton();
g_object_set(r, "sensitive", TRUE, NULL); // editable by default
g_signal_connect(r, "clicked", G_CALLBACK(buttonClicked), part);
appendPart(c, part, r, expand);
}
// yes, we need to do all this twice :|
static void checkboxToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data)
{
struct tablePart *part = (struct tablePart *) data;
GtkTreePath *path;
int row;
uiTableModel *m;
void *value;
int intval;
path = gtk_tree_path_new_from_string(pathstr);
row = gtk_tree_path_get_indices(path)[0];
gtk_tree_path_free(path);
m = part->tv->model;
value = (*(m->mh->CellValue))(m->mh, m, row, part->valueColumn);
intval = !uiTableModelTakeInt(value);
onEdited(part, part->valueColumn, pathstr, uiTableModelGiveInt(intval));
}
void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand)
{
struct tablePart *part;
GtkCellRenderer *r;
part = uiprivNew(struct tablePart);
part->type = partCheckbox;
part->valueColumn = modelColumn;
part->tv = c->tv;
r = gtk_cell_renderer_toggle_new();
g_object_set(r, "sensitive", TRUE, NULL); // editable by default
g_signal_connect(r, "toggled", G_CALLBACK(checkboxToggled), part);
appendPart(c, part, r, expand);
}
void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand)
{
struct tablePart *part;
part = uiprivNew(struct tablePart);
part->type = partProgressBar;
part->valueColumn = modelColumn;
part->tv = c->tv;
appendPart(c, part,
gtk_cell_renderer_progress_new(),
expand);
}
void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable)
{
struct tablePart *p;
p = (struct tablePart *) g_ptr_array_index(c->parts, part);
switch (p->type) {
case partImage:
case partProgressBar:
return;
case partButton:
case partCheckbox:
g_object_set(p->r, "sensitive", editable != 0, NULL);
return;
}
g_object_set(p->r, "editable", editable != 0, NULL);
}
void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn)
{
struct tablePart *p;
p = (struct tablePart *) g_ptr_array_index(c->parts, part);
p->colorColumn = modelColumn;
// TODO refresh table
}
uiUnixControlAllDefaultsExceptDestroy(uiTable)
static void uiTableDestroy(uiControl *c)
{
uiTable *t = uiTable(c);
// TODO
g_object_unref(t->widget);
uiFreeControl(uiControl(t));
}
uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name)
{
uiTableColumn *c;
c = uiprivNew(uiTableColumn);
c->c = gtk_tree_view_column_new();
gtk_tree_view_column_set_resizable(c->c, TRUE);
gtk_tree_view_column_set_title(c->c, name);
gtk_tree_view_append_column(t->tv, c->c);
c->tv = t; // TODO rename field to t, cascade
c->parts = g_ptr_array_new();
return c;
}
void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn)
{
t->backgroundColumn = modelColumn;
// TODO refresh table
}
uiTable *uiNewTable(uiTableModel *model)
{
uiTable *t;
uiUnixNewControl(uiTable, t);
t->model = model;
t->backgroundColumn = -1;
t->widget = gtk_scrolled_window_new(NULL, NULL);
t->scontainer = GTK_CONTAINER(t->widget);
t->sw = GTK_SCROLLED_WINDOW(t->widget);
gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN);
t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(t->model));
t->tv = GTK_TREE_VIEW(t->treeWidget);
// TODO set up t->tv
gtk_container_add(t->scontainer, t->treeWidget);
// and make the tree view visible; only the scrolled window's visibility is controlled by libui
gtk_widget_show(t->treeWidget);
return t;
}

View File

@ -3,10 +3,9 @@
// TODOs
// - it's a rather tight fit
// - selected row text color is white
// - resizing a column with a button in it crashes the program
// - selected row text color is white (TODO not on 3.22)
// - accessibility
// - right side too big?
// - right side too big? (TODO reverify)
#define cellRendererButtonType (cellRendererButton_get_type())
#define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton))
@ -58,6 +57,7 @@ static GtkSizeRequestMode cellRendererButton_get_request_mode(GtkCellRenderer *r
}
// this is basically what GtkCellRendererToggle did in 3.10 and does in 3.20, as well as what the Foreign Drawing gtk3-demo demo does
// TODO how does this seem to work with highlight on 3.22, and does that work with 3.10 too
static GtkStyleContext *setButtonStyle(GtkWidget *widget)
{
GtkStyleContext *base, *context;
@ -90,7 +90,56 @@ void unsetButtonStyle(GtkStyleContext *context)
g_object_unref(context);
}
// this is based on what GtkCellRendererText does
// this is based on what GtkCellRendererText in GTK+ 3.22.30 does
// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c)
static PangoLayout *cellRendererButtonPangoLayout(cellRendererButton *c, GtkWidget *widget)
{
PangoLayout *layout;
layout = gtk_widget_create_pango_layout(widget, c->text);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
pango_layout_set_width(layout, -1);
pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
return layout;
}
// this is based on what GtkCellRendererText in GTK+ 3.22.30 does
// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c)
static void cellRendererButtonSize(cellRendererButton *c, GtkWidget *widget, PangoLayout *layout, const GdkRectangle *cell_area, gint *xoff, gint *yoff, gint *width, gint *height)
{
PangoRectangle rect;
gint xpad, ypad;
gfloat xalign, yalign;
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad);
pango_layout_get_pixel_extents(layout, NULL, &rect);
if (rect.width > cell_area->width - (2 * xpad))
rect.width = cell_area->width - (2 * xpad);
if (rect.height > cell_area->height - (2 * ypad))
rect.height = cell_area->height - (2 * ypad);
gtk_cell_renderer_get_alignment(GTK_CELL_RENDERER(c), &xalign, &yalign);
if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
xalign = 1.0 - xalign;
if (xoff != NULL) {
*xoff = cell_area->width - (rect.width + (2 * xpad));
*xoff = (gint) ((gfloat) (*xoff) * xalign);
}
if (yoff != NULL) {
*yoff = cell_area->height - (rect.height + (2 * ypad));
*yoff = (gint) ((gfloat) (*yoff) * yalign);
if (*yoff < 0)
*yoff = 0;
}
if (width != NULL)
*width = rect.width - (2 * xpad);
if (height != NULL)
*height = rect.height - (2 * ypad);
}
// this is based on what GtkCellRendererText in GTK+ 3.22.30 does
// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c)
static void cellRendererButton_get_preferred_width(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural)
{
cellRendererButton *c = cellRendererButton(r);
@ -101,19 +150,21 @@ static void cellRendererButton_get_preferred_width(GtkCellRenderer *r, GtkWidget
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, NULL);
layout = gtk_widget_create_pango_layout(widget, c->text);
pango_layout_set_width(layout, -1);
layout = cellRendererButtonPangoLayout(c, widget);
pango_layout_get_extents(layout, NULL, &rect);
g_object_unref(layout);
out = 2 * xpad + PANGO_PIXELS_CEIL(rect.width);
out = PANGO_PIXELS_CEIL(rect.width) + (2 * xpad);
if (rect.x > 0)
out += rect.x;
if (minimum != NULL)
*minimum = out;
if (natural != NULL)
*natural = out;
}
// this is based on what GtkCellRendererText does
// this is based on what GtkCellRendererText in GTK+ 3.22.30 does
// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c)
static void cellRendererButton_get_preferred_height_for_width(GtkCellRenderer *r, GtkWidget *widget, gint width, gint *minimum, gint *natural)
{
cellRendererButton *c = cellRendererButton(r);
@ -124,19 +175,20 @@ static void cellRendererButton_get_preferred_height_for_width(GtkCellRenderer *r
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad);
layout = gtk_widget_create_pango_layout(widget, c->text);
pango_layout_set_width(layout, ((2 * xpad + width) * PANGO_SCALE));
layout = cellRendererButtonPangoLayout(c, widget);
pango_layout_set_width(layout, (width + (xpad * 2)) * PANGO_SCALE);
pango_layout_get_pixel_size(layout, NULL, &height);
g_object_unref(layout);
out = 2 * ypad + height;
out = height + (ypad * 2);
if (minimum != NULL)
*minimum = out;
if (natural != NULL)
*natural = out;
}
// this is basically what GtkCellRendererText does
// this is based on what GtkCellRendererText in GTK+ 3.22.30 does
// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c)
static void cellRendererButton_get_preferred_height(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural)
{
gint width;
@ -145,84 +197,65 @@ static void cellRendererButton_get_preferred_height(GtkCellRenderer *r, GtkWidge
gtk_cell_renderer_get_preferred_height_for_width(r, widget, width, minimum, natural);
}
// this is based on what GtkCellRendererText does
// this is based on what GtkCellRendererText in GTK+ 3.22.30 does
// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c)
static void cellRendererButton_get_aligned_area(GtkCellRenderer *r, GtkWidget *widget, GtkCellRendererState flags, const GdkRectangle *cell_area, GdkRectangle *aligned_area)
{
cellRendererButton *c = cellRendererButton(r);
gint xpad, ypad;
PangoLayout *layout;
PangoRectangle rect;
gfloat xalign, yalign;
gint xoffset, yoffset;
gint xoff, yoff;
gint width, height;
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad);
layout = cellRendererButtonPangoLayout(c, widget);
cellRendererButtonSize(c, widget, layout, cell_area,
&xoff, &yoff, &width, &height);
layout = gtk_widget_create_pango_layout(widget, c->text);
pango_layout_set_width(layout, -1);
pango_layout_get_pixel_extents(layout, NULL, &rect);
xoffset = 0;
yoffset = 0;
if (cell_area != NULL) {
gtk_cell_renderer_get_alignment(GTK_CELL_RENDERER(c), &xalign, &yalign);
xoffset = cell_area->width - (2 * xpad + rect.width);
// use explicit casts just to be safe
if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
xoffset = ((gdouble) xoffset) * (1.0 - xalign);
else
xoffset *= ((gdouble) xoffset) * xalign;
yoffset = yalign * (cell_area->height - (2 * ypad + rect.height));
yoffset = MAX(yoffset, 0);
}
aligned_area->x = cell_area->x + xoffset;
aligned_area->y = cell_area->y + yoffset;
aligned_area->width = 2 * xpad + rect.width;
aligned_area->height = 2 * ypad + rect.height;
aligned_area->x = cell_area->x + xoff;
aligned_area->y = cell_area->y + yoff;
aligned_area->width = width;
aligned_area->height = height;
g_object_unref(layout);
}
// this is based on both what GtkCellRendererText does and what GtkCellRendererToggle does
// this is based on both what GtkCellRendererText on 3.22.30 does and what GtkCellRendererToggle does (TODO verify the latter; both on 3.10.9)
static void cellRendererButton_render(GtkCellRenderer *r, cairo_t *cr, GtkWidget *widget, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags)
{
cellRendererButton *c = cellRendererButton(r);
gint xpad, ypad;
GdkRectangle alignedArea;
gint xoffset, yoffset;
gint xoff, yoff;
GtkStyleContext *context;
PangoLayout *layout;
PangoRectangle rect;
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad);
gtk_cell_renderer_get_aligned_area(GTK_CELL_RENDERER(c), widget, flags, cell_area, &alignedArea);
xoffset = alignedArea.x - cell_area->x;
yoffset = alignedArea.y - cell_area->y;
layout = cellRendererButtonPangoLayout(c, widget);
cellRendererButtonSize(c, widget, layout, cell_area,
&xoff, &yoff, NULL, NULL);
context = setButtonStyle(widget);
layout = gtk_widget_create_pango_layout(widget, c->text);
gtk_render_background(context, cr,
background_area->x + xoffset + xpad,
background_area->y + yoffset + ypad,
background_area->width - 2 * xpad,
background_area->height - 2 * ypad);
background_area->x + xpad,
background_area->y + ypad,
background_area->width - (xpad * 2),
background_area->height - (ypad * 2));
gtk_render_frame(context, cr,
background_area->x + xoffset + xpad,
background_area->y + yoffset + ypad,
background_area->width - 2 * xpad,
background_area->height - 2 * ypad);
background_area->x + xpad,
background_area->y + ypad,
background_area->width - (xpad * 2),
background_area->height - (ypad * 2));
pango_layout_set_width(layout, -1);
pango_layout_get_pixel_extents(layout, NULL, &rect);
xoffset -= rect.x;
xoff -= rect.x;
gtk_render_layout(context, cr,
cell_area->x + xoffset + xpad,
cell_area->y + yoffset + ypad,
cell_area->x + xoff + xpad,
cell_area->y + yoff + ypad,
layout);
g_object_unref(layout);
unsetButtonStyle(context);
g_object_unref(layout);
}
static guint clickedSignal;

View File

@ -34,24 +34,26 @@ void uiFreeImage(uiImage *i)
uiprivFree(i);
}
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride)
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride)
{
cairo_surface_t *cs;
unsigned char *buf, *p;
uint8_t *src = (uint8_t *) pixels;
int cstride;
int y;
int cByteStride;
int n;
cstride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth);
buf = (unsigned char *) uiprivAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]");
// unfortunately for optimal performance cairo expects its own stride values and we will have to reconcile them if they differ
cByteStride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth);
buf = (unsigned char *) uiprivAlloc((cByteStride * pixelHeight) * sizeof (unsigned char), "unsigned char[]");
p = buf;
for (y = 0; y < pixelStride * pixelHeight; y += pixelStride) {
memmove(p, src + y, cstride);
p += cstride;
for (n = 0; n < byteStride * pixelHeight; n += byteStride) {
memmove(p, src + n, cByteStride);
p += cByteStride;
}
// also note that stride here is also in bytes
cs = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
pixelWidth, pixelHeight,
cstride);
cByteStride);
if (cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS)
/* TODO */;
cairo_surface_flush(cs);

View File

@ -1,6 +1,9 @@
// 11 june 2015
#include "uipriv_unix.h"
// LONGTERM:
// - in GTK+ 3.22 at least, running both a GtkProgressBar and a GtkCellRendererProgress in pulse mode with our code will cause the former to slow down and eventually stop, and I can't tell why at all
struct uiProgressBar {
uiUnixControl c;
GtkWidget *widget;

522
unix/table.c Normal file
View File

@ -0,0 +1,522 @@
// 26 june 2016
#include "uipriv_unix.h"
#include "table.h"
// TODO with GDK_SCALE set to 2 the 32x32 images are scaled up to 64x64?
struct uiTable {
uiUnixControl c;
GtkWidget *widget;
GtkContainer *scontainer;
GtkScrolledWindow *sw;
GtkWidget *treeWidget;
GtkTreeView *tv;
uiTableModel *model;
GPtrArray *columnParams;
int backgroundColumn;
// keys are struct rowcol, values are gint
// TODO document this properly
GHashTable *indeterminatePositions;
guint indeterminateTimer;
};
// use the same size as GtkFileChooserWidget's treeview
// TODO refresh when icon theme changes
// TODO doesn't work when scaled?
// TODO is this even necessary?
static void setImageSize(GtkCellRenderer *r)
{
gint size;
gint width, height;
gint xpad, ypad;
size = 16; // fallback used by GtkFileChooserWidget
if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height) != FALSE)
size = MAX(width, height);
gtk_cell_renderer_get_padding(r, &xpad, &ypad);
gtk_cell_renderer_set_fixed_size(r,
2 * xpad + size,
2 * ypad + size);
}
static void applyColor(GtkTreeModel *m, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet)
{
GValue value = G_VALUE_INIT;
GdkRGBA *rgba;
gtk_tree_model_get_value(m, iter, modelColumn, &value);
rgba = (GdkRGBA *) g_value_get_boxed(&value);
if (rgba != NULL)
g_object_set(r, prop, rgba, NULL);
else
g_object_set(r, propSet, FALSE, NULL);
g_value_unset(&value);
}
static void setEditable(uiTableModel *m, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop)
{
GtkTreePath *path;
int row;
gboolean editable;
// TODO avoid the need for this
path = gtk_tree_model_get_path(GTK_TREE_MODEL(m), iter);
row = gtk_tree_path_get_indices(path)[0];
editable = uiprivTableModelCellEditable(m, row, modelColumn) != 0;
g_object_set(r, prop, editable, NULL);
}
static void applyBackgroundColor(uiTable *t, GtkTreeModel *m, GtkTreeIter *iter, GtkCellRenderer *r)
{
if (t->backgroundColumn != -1)
applyColor(m, iter, t->backgroundColumn,
r, "cell-background-rgba", "cell-background-set");
}
static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiTableValue *tvalue, GtkTreeIter *iter)
{
GtkTreePath *path;
int row;
path = gtk_tree_path_new_from_string(pathstr);
row = gtk_tree_path_get_indices(path)[0];
if (iter != NULL)
gtk_tree_model_get_iter(GTK_TREE_MODEL(m), iter, path);
gtk_tree_path_free(path);
uiprivTableModelSetCellValue(m, row, column, tvalue);
}
struct textColumnParams {
uiTable *t;
uiTableModel *m;
int modelColumn;
int editableColumn;
uiTableTextColumnOptionalParams params;
};
static void textColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data)
{
struct textColumnParams *p = (struct textColumnParams *) data;
GValue value = G_VALUE_INIT;
const gchar *str;
gtk_tree_model_get_value(m, iter, p->modelColumn, &value);
str = g_value_get_string(&value);
g_object_set(r, "text", str, NULL);
g_value_unset(&value);
setEditable(p->m, iter, p->editableColumn, r, "editable");
if (p->params.ColorModelColumn != -1)
applyColor(m, iter, p->params.ColorModelColumn,
r, "foreground-rgba", "foreground-set");
applyBackgroundColor(p->t, m, iter, r);
}
static void textColumnEdited(GtkCellRendererText *r, gchar *path, gchar *newText, gpointer data)
{
struct textColumnParams *p = (struct textColumnParams *) data;
uiTableValue *tvalue;
GtkTreeIter iter;
tvalue = uiNewTableValueString(newText);
onEdited(p->m, p->modelColumn, path, tvalue, &iter);
uiFreeTableValue(tvalue);
// and update the column TODO copy comment here
textColumnDataFunc(NULL, GTK_CELL_RENDERER(r), GTK_TREE_MODEL(p->m), &iter, data);
}
struct imageColumnParams {
uiTable *t;
int modelColumn;
};
static void imageColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data)
{
struct imageColumnParams *p = (struct imageColumnParams *) data;
GValue value = G_VALUE_INIT;
uiImage *img;
//TODO setImageSize(r);
gtk_tree_model_get_value(m, iter, p->modelColumn, &value);
img = (uiImage *) g_value_get_pointer(&value);
g_object_set(r, "surface",
uiprivImageAppropriateSurface(img, p->t->treeWidget),
NULL);
g_value_unset(&value);
applyBackgroundColor(p->t, m, iter, r);
}
struct checkboxColumnParams {
uiTable *t;
uiTableModel *m;
int modelColumn;
int editableColumn;
};
static void checkboxColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data)
{
struct checkboxColumnParams *p = (struct checkboxColumnParams *) data;
GValue value = G_VALUE_INIT;
gboolean active;
gtk_tree_model_get_value(m, iter, p->modelColumn, &value);
active = g_value_get_int(&value) != 0;
g_object_set(r, "active", active, NULL);
g_value_unset(&value);
setEditable(p->m, iter, p->editableColumn, r, "activatable");
applyBackgroundColor(p->t, m, iter, r);
}
static void checkboxColumnToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data)
{
struct checkboxColumnParams *p = (struct checkboxColumnParams *) data;
GValue value = G_VALUE_INIT;
int v;
uiTableValue *tvalue;
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_string(pathstr);
gtk_tree_model_get_iter(GTK_TREE_MODEL(p->m), &iter, path);
gtk_tree_path_free(path);
gtk_tree_model_get_value(GTK_TREE_MODEL(p->m), &iter, p->modelColumn, &value);
v = g_value_get_int(&value);
g_value_unset(&value);
tvalue = uiNewTableValueInt(!v);
onEdited(p->m, p->modelColumn, pathstr, tvalue, NULL);
uiFreeTableValue(tvalue);
// and update the column TODO copy comment here
// TODO avoid fetching the model data twice
checkboxColumnDataFunc(NULL, GTK_CELL_RENDERER(r), GTK_TREE_MODEL(p->m), &iter, data);
}
struct progressBarColumnParams {
uiTable *t;
int modelColumn;
};
struct rowcol {
int row;
int col;
};
static guint rowcolHash(gconstpointer key)
{
const struct rowcol *rc = (const struct rowcol *) key;
guint row, col;
row = (guint) (rc->row);
col = (guint) (rc->col);
return row ^ col;
}
static gboolean rowcolEqual(gconstpointer a, gconstpointer b)
{
const struct rowcol *ra = (const struct rowcol *) a;
const struct rowcol *rb = (const struct rowcol *) b;
return (ra->row == rb->row) && (ra->col == rb->col);
}
static void pulseOne(gpointer key, gpointer value, gpointer data)
{
uiTable *t = uiTable(data);
struct rowcol *rc = (struct rowcol *) key;
// TODO this is bad: it produces changed handlers for every table because that's how GtkTreeModel works, yet this is per-table because that's how it works
// however, a proper fix would require decoupling progress from normal integers, which we could do...
uiTableModelRowChanged(t->model, rc->row);
}
static gboolean indeterminatePulse(gpointer data)
{
uiTable *t = uiTable(data);
g_hash_table_foreach(t->indeterminatePositions, pulseOne, t);
return TRUE;
}
static void progressBarColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data)
{
struct progressBarColumnParams *p = (struct progressBarColumnParams *) data;
GValue value = G_VALUE_INIT;
int pval;
struct rowcol *rc;
gint *val;
GtkTreePath *path;
gtk_tree_model_get_value(m, iter, p->modelColumn, &value);
pval = g_value_get_int(&value);
rc = uiprivNew(struct rowcol);
// TODO avoid the need for this
path = gtk_tree_model_get_path(GTK_TREE_MODEL(m), iter);
rc->row = gtk_tree_path_get_indices(path)[0];
rc->col = p->modelColumn;
val = (gint *) g_hash_table_lookup(p->t->indeterminatePositions, rc);
if (pval == -1) {
if (val == NULL) {
val = uiprivNew(gint);
*val = 1;
g_hash_table_insert(p->t->indeterminatePositions, rc, val);
} else {
uiprivFree(rc);
(*val)++;
if (*val == G_MAXINT)
*val = 1;
}
g_object_set(r,
"pulse", *val,
NULL);
if (p->t->indeterminateTimer == 0)
// TODO verify the timeout
p->t->indeterminateTimer = g_timeout_add(100, indeterminatePulse, p->t);
} else {
if (val != NULL) {
g_hash_table_remove(p->t->indeterminatePositions, rc);
if (g_hash_table_size(p->t->indeterminatePositions) == 0) {
g_source_remove(p->t->indeterminateTimer);
p->t->indeterminateTimer = 0;
}
}
uiprivFree(rc);
g_object_set(r,
"pulse", -1,
"value", pval,
NULL);
}
g_value_unset(&value);
applyBackgroundColor(p->t, m, iter, r);
}
struct buttonColumnParams {
uiTable *t;
uiTableModel *m;
int modelColumn;
int clickableColumn;
};
static void buttonColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data)
{
struct buttonColumnParams *p = (struct buttonColumnParams *) data;
GValue value = G_VALUE_INIT;
const gchar *str;
gtk_tree_model_get_value(m, iter, p->modelColumn, &value);
str = g_value_get_string(&value);
g_object_set(r, "text", str, NULL);
g_value_unset(&value);
setEditable(p->m, iter, p->clickableColumn, r, "sensitive");
applyBackgroundColor(p->t, m, iter, r);
}
// TODO wrong type here
static void buttonColumnClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data)
{
struct buttonColumnParams *p = (struct buttonColumnParams *) data;
onEdited(p->m, p->modelColumn, pathstr, NULL, NULL);
}
static GtkTreeViewColumn *addColumn(uiTable *t, const char *name)
{
GtkTreeViewColumn *c;
c = gtk_tree_view_column_new();
gtk_tree_view_column_set_resizable(c, TRUE);
gtk_tree_view_column_set_title(c, name);
gtk_tree_view_append_column(t->tv, c);
return c;
}
static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
struct textColumnParams *p;
GtkCellRenderer *r;
p = uiprivNew(struct textColumnParams);
p->t = t;
// TODO get rid of these fields AND rename t->model in favor of t->m
p->m = t->model;
p->modelColumn = textModelColumn;
p->editableColumn = textEditableModelColumn;
if (textParams != NULL)
p->params = *textParams;
else
p->params = uiprivDefaultTextColumnOptionalParams;
r = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(c, r, TRUE);
gtk_tree_view_column_set_cell_data_func(c, r, textColumnDataFunc, p, NULL);
g_signal_connect(r, "edited", G_CALLBACK(textColumnEdited), p);
g_ptr_array_add(t->columnParams, p);
}
// TODO rename modelCOlumn and params everywhere
void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
GtkTreeViewColumn *c;
c = addColumn(t, name);
addTextColumn(t, c, textModelColumn, textEditableModelColumn, textParams);
}
static void addImageColumn(uiTable *t, GtkTreeViewColumn *c, int imageModelColumn)
{
struct imageColumnParams *p;
GtkCellRenderer *r;
p = uiprivNew(struct imageColumnParams);
p->t = t;
p->modelColumn = imageModelColumn;
r = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_start(c, r, FALSE);
gtk_tree_view_column_set_cell_data_func(c, r, imageColumnDataFunc, p, NULL);
g_ptr_array_add(t->columnParams, p);
}
void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn)
{
GtkTreeViewColumn *c;
c = addColumn(t, name);
addImageColumn(t, c, imageModelColumn);
}
void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
GtkTreeViewColumn *c;
c = addColumn(t, name);
addImageColumn(t, c, imageModelColumn);
addTextColumn(t, c, textModelColumn, textEditableModelColumn, textParams);
}
static void addCheckboxColumn(uiTable *t, GtkTreeViewColumn *c, int checkboxModelColumn, int checkboxEditableModelColumn)
{
struct checkboxColumnParams *p;
GtkCellRenderer *r;
p = uiprivNew(struct checkboxColumnParams);
p->t = t;
p->m = t->model;
p->modelColumn = checkboxModelColumn;
p->editableColumn = checkboxEditableModelColumn;
r = gtk_cell_renderer_toggle_new();
gtk_tree_view_column_pack_start(c, r, FALSE);
gtk_tree_view_column_set_cell_data_func(c, r, checkboxColumnDataFunc, p, NULL);
g_signal_connect(r, "toggled", G_CALLBACK(checkboxColumnToggled), p);
g_ptr_array_add(t->columnParams, p);
}
void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn)
{
GtkTreeViewColumn *c;
c = addColumn(t, name);
addCheckboxColumn(t, c, checkboxModelColumn, checkboxEditableModelColumn);
}
void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
GtkTreeViewColumn *c;
c = addColumn(t, name);
addCheckboxColumn(t, c, checkboxModelColumn, checkboxEditableModelColumn);
addTextColumn(t, c, textModelColumn, textEditableModelColumn, textParams);
}
void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn)
{
GtkTreeViewColumn *c;
struct progressBarColumnParams *p;
GtkCellRenderer *r;
c = addColumn(t, name);
p = uiprivNew(struct progressBarColumnParams);
p->t = t;
// TODO make progress and progressBar consistent everywhere
p->modelColumn = progressModelColumn;
r = gtk_cell_renderer_progress_new();
gtk_tree_view_column_pack_start(c, r, TRUE);
gtk_tree_view_column_set_cell_data_func(c, r, progressBarColumnDataFunc, p, NULL);
g_ptr_array_add(t->columnParams, p);
}
void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn)
{
GtkTreeViewColumn *c;
struct buttonColumnParams *p;
GtkCellRenderer *r;
c = addColumn(t, name);
p = uiprivNew(struct buttonColumnParams);
p->t = t;
p->m = t->model;
p->modelColumn = buttonModelColumn;
p->clickableColumn = buttonClickableModelColumn;
r = uiprivNewCellRendererButton();
gtk_tree_view_column_pack_start(c, r, TRUE);
gtk_tree_view_column_set_cell_data_func(c, r, buttonColumnDataFunc, p, NULL);
g_signal_connect(r, "clicked", G_CALLBACK(buttonColumnClicked), p);
g_ptr_array_add(t->columnParams, p);
}
uiUnixControlAllDefaultsExceptDestroy(uiTable)
static void uiTableDestroy(uiControl *c)
{
uiTable *t = uiTable(c);
guint i;
for (i = 0; i < t->columnParams->len; i++)
uiprivFree(g_ptr_array_index(t->columnParams, i));
g_ptr_array_free(t->columnParams, TRUE);
if (g_hash_table_size(t->indeterminatePositions) != 0)
g_source_remove(t->indeterminateTimer);
g_hash_table_destroy(t->indeterminatePositions);
g_object_unref(t->widget);
uiFreeControl(uiControl(t));
}
uiTable *uiNewTable(uiTableParams *p)
{
uiTable *t;
uiUnixNewControl(uiTable, t);
t->model = p->Model;
t->columnParams = g_ptr_array_new();
t->backgroundColumn = p->RowBackgroundColorModelColumn;
t->widget = gtk_scrolled_window_new(NULL, NULL);
t->scontainer = GTK_CONTAINER(t->widget);
t->sw = GTK_SCROLLED_WINDOW(t->widget);
gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN);
t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(t->model));
t->tv = GTK_TREE_VIEW(t->treeWidget);
// TODO set up t->tv
gtk_container_add(t->scontainer, t->treeWidget);
// and make the tree view visible; only the scrolled window's visibility is controlled by libui
gtk_widget_show(t->treeWidget);
t->indeterminatePositions = g_hash_table_new_full(rowcolHash, rowcolEqual,
uiprivFree, uiprivFree);
return t;
}

19
unix/table.h Normal file
View File

@ -0,0 +1,19 @@
// 4 june 2018
#include "../common/table.h"
// tablemodel.c
#define uiTableModelType (uiTableModel_get_type())
#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel))
#define isuiTableModel(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType))
#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass))
#define isuiTableModelClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel))
#define getuiTableModelClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass))
typedef struct uiTableModelClass uiTableModelClass;
struct uiTableModel {
GObject parent_instance;
uiTableModelHandler *mh;
};
struct uiTableModelClass {
GObjectClass parent_class;
};
extern GType uiTableModel_get_type(void);

288
unix/tablemodel.c Normal file
View File

@ -0,0 +1,288 @@
// 26 june 2016
#include "uipriv_unix.h"
#include "table.h"
static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface);
G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init))
static void uiTableModel_init(uiTableModel *m)
{
// nothing to do
}
static void uiTableModel_dispose(GObject *obj)
{
G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj);
}
static void uiTableModel_finalize(GObject *obj)
{
G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj);
}
static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mm)
{
return GTK_TREE_MODEL_LIST_ONLY;
}
static gint uiTableModel_get_n_columns(GtkTreeModel *mm)
{
uiTableModel *m = uiTableModel(mm);
return uiprivTableModelNumColumns(m);
}
static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index)
{
uiTableModel *m = uiTableModel(mm);
switch (uiprivTableModelColumnType(m, index)) {
case uiTableValueTypeString:
return G_TYPE_STRING;
case uiTableValueTypeImage:
return G_TYPE_POINTER;
case uiTableValueTypeInt:
return G_TYPE_INT;
case uiTableValueTypeColor:
return GDK_TYPE_RGBA;
}
// TODO
return G_TYPE_INVALID;
}
#define STAMP_GOOD 0x1234
#define STAMP_BAD 0x5678
static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreePath *path)
{
uiTableModel *m = uiTableModel(mm);
gint row;
if (gtk_tree_path_get_depth(path) != 1)
goto bad;
row = gtk_tree_path_get_indices(path)[0];
if (row < 0)
goto bad;
if (row >= uiprivTableModelNumRows(m))
goto bad;
iter->stamp = STAMP_GOOD;
iter->user_data = GINT_TO_POINTER(row);
return TRUE;
bad:
iter->stamp = STAMP_BAD;
return FALSE;
}
// GtkListStore returns NULL on error; let's do that too
static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter *iter)
{
gint row;
if (iter->stamp != STAMP_GOOD)
return NULL;
row = GPOINTER_TO_INT(iter->user_data);
return gtk_tree_path_new_from_indices(row, -1);
}
// GtkListStore leaves value empty on failure; let's do the same
static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint column, GValue *value)
{
uiTableModel *m = uiTableModel(mm);
gint row;
uiTableValue *tvalue;
double r, g, b, a;
GdkRGBA rgba;
if (iter->stamp != STAMP_GOOD)
return;
row = GPOINTER_TO_INT(iter->user_data);
tvalue = uiprivTableModelCellValue(m, row, column);
switch (uiprivTableModelColumnType(m, column)) {
case uiTableValueTypeString:
g_value_init(value, G_TYPE_STRING);
g_value_set_string(value, uiTableValueString(tvalue));
uiFreeTableValue(tvalue);
return;
case uiTableValueTypeImage:
g_value_init(value, G_TYPE_POINTER);
g_value_set_pointer(value, uiTableValueImage(tvalue));
uiFreeTableValue(tvalue);
return;
case uiTableValueTypeInt:
g_value_init(value, G_TYPE_INT);
g_value_set_int(value, uiTableValueInt(tvalue));
uiFreeTableValue(tvalue);
return;
case uiTableValueTypeColor:
g_value_init(value, GDK_TYPE_RGBA);
if (tvalue == NULL) {
g_value_set_boxed(value, NULL);
return;
}
uiTableValueColor(tvalue, &r, &g, &b, &a);
uiFreeTableValue(tvalue);
rgba.red = r;
rgba.green = g;
rgba.blue = b;
rgba.alpha = a;
g_value_set_boxed(value, &rgba);
return;
}
// TODO
}
static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter)
{
uiTableModel *m = uiTableModel(mm);
gint row;
if (iter->stamp != STAMP_GOOD)
return FALSE;
row = GPOINTER_TO_INT(iter->user_data);
row++;
if (row >= uiprivTableModelNumRows(m)) {
iter->stamp = STAMP_BAD;
return FALSE;
}
iter->user_data = GINT_TO_POINTER(row);
return TRUE;
}
static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter)
{
gint row;
if (iter->stamp != STAMP_GOOD)
return FALSE;
row = GPOINTER_TO_INT(iter->user_data);
row--;
if (row < 0) {
iter->stamp = STAMP_BAD;
return FALSE;
}
iter->user_data = GINT_TO_POINTER(row);
return TRUE;
}
static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent)
{
return gtk_tree_model_iter_nth_child(mm, iter, parent, 0);
}
static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter)
{
return FALSE;
}
static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter)
{
uiTableModel *m = uiTableModel(mm);
if (iter != NULL)
return 0;
return uiprivTableModelNumRows(m);
}
static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
{
uiTableModel *m = uiTableModel(mm);
if (iter->stamp != STAMP_GOOD)
return FALSE;
if (parent != NULL)
goto bad;
if (n < 0)
goto bad;
if (n >= uiprivTableModelNumRows(m))
goto bad;
iter->stamp = STAMP_GOOD;
iter->user_data = GINT_TO_POINTER(n);
return TRUE;
bad:
iter->stamp = STAMP_BAD;
return FALSE;
}
gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *child)
{
iter->stamp = STAMP_BAD;
return FALSE;
}
static void uiTableModel_class_init(uiTableModelClass *class)
{
G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose;
G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize;
}
static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface)
{
iface->get_flags = uiTableModel_get_flags;
iface->get_n_columns = uiTableModel_get_n_columns;
iface->get_column_type = uiTableModel_get_column_type;
iface->get_iter = uiTableModel_get_iter;
iface->get_path = uiTableModel_get_path;
iface->get_value = uiTableModel_get_value;
iface->iter_next = uiTableModel_iter_next;
iface->iter_previous = uiTableModel_iter_previous;
iface->iter_children = uiTableModel_iter_children;
iface->iter_has_child = uiTableModel_iter_has_child;
iface->iter_n_children = uiTableModel_iter_n_children;
iface->iter_nth_child = uiTableModel_iter_nth_child;
iface->iter_parent = uiTableModel_iter_parent;
// don't specify ref_node() or unref_node()
}
uiTableModel *uiNewTableModel(uiTableModelHandler *mh)
{
uiTableModel *m;
m = uiTableModel(g_object_new(uiTableModelType, NULL));
m->mh = mh;
return m;
}
void uiFreeTableModel(uiTableModel *m)
{
g_object_unref(m);
}
void uiTableModelRowInserted(uiTableModel *m, int newIndex)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_indices(newIndex, -1);
iter.stamp = STAMP_GOOD;
iter.user_data = GINT_TO_POINTER(newIndex);
gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter);
gtk_tree_path_free(path);
}
void uiTableModelRowChanged(uiTableModel *m, int index)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_indices(index, -1);
iter.stamp = STAMP_GOOD;
iter.user_data = GINT_TO_POINTER(index);
gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter);
gtk_tree_path_free(path);
}
void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
{
GtkTreePath *path;
path = gtk_tree_path_new_from_indices(oldIndex, -1);
gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path);
gtk_tree_path_free(path);
}
uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m)
{
return m->mh;
}

View File

@ -48,7 +48,6 @@ extern uiDrawContext *uiprivNewContext(cairo_t *cr, GtkStyleContext *style);
extern void uiprivFreeContext(uiDrawContext *);
// image.c
/*TODO remove this*/typedef struct uiImage uiImage;
extern cairo_surface_t *uiprivImageAppropriateSurface(uiImage *i, GtkWidget *w);
// cellrendererbutton.c

View File

@ -34,6 +34,7 @@ list(APPEND _LIBUI_SOURCES
windows/graphemes.cpp
windows/grid.cpp
windows/group.cpp
windows/image.cpp
windows/init.cpp
windows/label.cpp
windows/main.cpp
@ -49,6 +50,11 @@ list(APPEND _LIBUI_SOURCES
windows/spinbox.cpp
windows/stddialogs.cpp
windows/tab.cpp
windows/table.cpp
windows/tabledispinfo.cpp
windows/tabledraw.cpp
windows/tableediting.cpp
windows/tablemetrics.cpp
windows/tabpage.cpp
windows/text.cpp
windows/utf16.cpp
@ -76,7 +82,7 @@ set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE)
# TODO prune this list
set(_LIBUI_LIBS
user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid
user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid windowscodecs
PARENT_SCOPE)
if(NOT MSVC)

226
windows/image.cpp Normal file
View File

@ -0,0 +1,226 @@
#include "uipriv_windows.hpp"
IWICImagingFactory *uiprivWICFactory = NULL;
HRESULT uiprivInitImage(void)
{
return CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory, (void **) (&uiprivWICFactory));
}
void uiprivUninitImage(void)
{
uiprivWICFactory->Release();
uiprivWICFactory = NULL;
}
struct uiImage {
double width;
double height;
std::vector<IWICBitmap *> *bitmaps;
};
uiImage *uiNewImage(double width, double height)
{
uiImage *i;
i = uiprivNew(uiImage);
i->width = width;
i->height = height;
i->bitmaps = new std::vector<IWICBitmap *>;
return i;
}
void uiFreeImage(uiImage *i)
{
for (IWICBitmap *b : *(i->bitmaps))
b->Release();
delete i->bitmaps;
uiprivFree(i);
}
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride)
{
IWICBitmap *b;
HRESULT hr;
hr = uiprivWICFactory->CreateBitmapFromMemory(pixelWidth, pixelHeight,
GUID_WICPixelFormat32bppRGBA, byteStride,
byteStride * pixelHeight, (BYTE *) pixels,
&b);
if (hr != S_OK)
logHRESULT(L"error calling CreateBitmapFromMemory() in uiImageAppend()", hr);
i->bitmaps->push_back(b);
}
struct matcher {
IWICBitmap *best;
int distX;
int distY;
int targetX;
int targetY;
bool foundLarger;
};
// TODO is this the right algorithm?
static void match(IWICBitmap *b, struct matcher *m)
{
UINT ux, uy;
int x, y;
int x2, y2;
HRESULT hr;
hr = b->GetSize(&ux, &uy);
if (hr != S_OK)
logHRESULT(L"error calling GetSize() in match()", hr);
x = ux;
y = uy;
if (m->best == NULL)
goto writeMatch;
if (x < m->targetX && y < m->targetY)
if (m->foundLarger)
// always prefer larger ones
return;
if (x >= m->targetX && y >= m->targetY && !m->foundLarger)
// we set foundLarger below
goto writeMatch;
// TODO
#define abs(x) ((x) < 0 ? -(x) : (x))
x2 = abs(m->targetX - x);
y2 = abs(m->targetY - y);
if (x2 < m->distX && y2 < m->distY)
goto writeMatch;
// TODO weight one dimension? threshhold?
return;
writeMatch:
// must set this here too; otherwise the first image will never have ths set
if (x >= m->targetX && y >= m->targetY && !m->foundLarger)
m->foundLarger = true;
m->best = b;
m->distX = abs(m->targetX - x);
m->distY = abs(m->targetY - y);
}
IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc)
{
struct matcher m;
m.best = NULL;
m.distX = INT_MAX;
m.distY = INT_MAX;
// TODO explain this
m.targetX = MulDiv(i->width, GetDeviceCaps(dc, LOGPIXELSX), 96);
m.targetY = MulDiv(i->height, GetDeviceCaps(dc, LOGPIXELSY), 96);
m.foundLarger = false;
for (IWICBitmap *b : *(i->bitmaps))
match(b, &m);
return m.best;
}
// TODO this needs to center images if the given size is not the same aspect ratio
HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb)
{
UINT ux, uy;
int x, y;
IWICBitmapSource *src;
BITMAPINFO bmi;
VOID *bits;
BITMAP bmp;
HRESULT hr;
hr = b->GetSize(&ux, &uy);
if (hr != S_OK)
return hr;
x = ux;
y = uy;
if (width == 0)
width = x;
if (height == 0)
height = y;
// special case: don't invoke a scaler if the size is the same
if (width == x && height == y) {
b->AddRef(); // for the Release() later
src = b;
} else {
IWICBitmapScaler *scaler;
WICPixelFormatGUID guid;
IWICFormatConverter *conv;
hr = uiprivWICFactory->CreateBitmapScaler(&scaler);
if (hr != S_OK)
return hr;
hr = scaler->Initialize(b, width, height,
// according to https://stackoverflow.com/questions/4250738/is-stretchblt-halftone-bilinear-for-all-scaling, this is what StretchBlt(COLORONCOLOR) does (with COLORONCOLOR being what's supported by AlphaBlend())
WICBitmapInterpolationModeNearestNeighbor);
if (hr != S_OK) {
scaler->Release();
return hr;
}
// But we are not done yet! IWICBitmapScaler can use an
// entirely different pixel format than what we gave it,
// and by extension, what GDI wants. See also:
// - https://stackoverflow.com/questions/28323228/iwicbitmapscaler-doesnt-work-for-96bpprgbfloat-format
// - https://github.com/Microsoft/DirectXTex/blob/0d94e9469bc3e6080a71145f35efa559f8f2e522/DirectXTex/DirectXTexResize.cpp#L83
hr = scaler->GetPixelFormat(&guid);
if (hr != S_OK) {
scaler->Release();
return hr;
}
if (IsEqualGUID(guid, GUID_WICPixelFormat32bppRGBA))
src = scaler;
else {
hr = uiprivWICFactory->CreateFormatConverter(&conv);
if (hr != S_OK) {
scaler->Release();
return hr;
}
hr = conv->Initialize(scaler, GUID_WICPixelFormat32bppRGBA,
// TODO is the dither type correct in all cases?
WICBitmapDitherTypeNone, NULL, 0, WICBitmapPaletteTypeMedianCut);
scaler->Release();
if (hr != S_OK) {
conv->Release();
return hr;
}
src = conv;
}
}
ZeroMemory(&bmi, sizeof (BITMAPINFO));
bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -((int) height);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
*hb = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS,
&bits, NULL, 0);
if (*hb == NULL) {
logLastError(L"CreateDIBSection()");
hr = E_FAIL;
goto fail;
}
// now we need to figure out the stride of the image data GDI gave us
// TODO find out if CreateDIBSection() fills that in bmi for us
// TODO fill in the error returns here too
if (GetObject(*hb, sizeof (BITMAP), &bmp) == 0)
logLastError(L"error calling GetObject() in uiprivWICToGDI()");
hr = src->CopyPixels(NULL, bmp.bmWidthBytes,
bmp.bmWidthBytes * bmp.bmHeight, (BYTE *) bits);
fail:
if (*hb != NULL && hr != S_OK) {
// don't bother with the error returned here
DeleteObject(*hb);
*hb = NULL;
}
src->Release();
return hr;
}

View File

@ -131,11 +131,16 @@ const char *uiInit(uiInitOptions *o)
if (registerD2DScratchClass(hDefaultIcon, hDefaultCursor) == 0)
return ieLastErr("initializing D2D scratch window class");
hr = uiprivInitImage();
if (hr != S_OK)
return ieHRESULT("initializing WIC", hr);
return NULL;
}
void uiUninit(void)
{
uiprivUninitImage();
uninitMenus();
unregisterD2DScratchClass();
unregisterMessageFilter();

522
windows/table.cpp Normal file
View File

@ -0,0 +1,522 @@
#include "uipriv_windows.hpp"
#include "table.hpp"
// general TODOs:
// - tooltips don't work properly on columns with icons (the listview always thinks there's enough room for a short label because it's not taking the icon into account); is this a bug in our LVN_GETDISPINFO handler or something else?
// - should clicking on some other column of the same row, even one that doesn't edit, cancel editing?
// - implement keyboard accessibility
// - implement accessibility in general (Dynamic Annotations maybe?)
// - if I didn't handle these already: "drawing focus rects here, subitem navigation and activation with the keyboard"
uiTableModel *uiNewTableModel(uiTableModelHandler *mh)
{
uiTableModel *m;
m = uiprivNew(uiTableModel);
m->mh = mh;
m->tables = new std::vector<uiTable *>;
return m;
}
void uiFreeTableModel(uiTableModel *m)
{
delete m->tables;
uiprivFree(m);
}
// TODO document that when this is called, the model must return the new row count when asked
void uiTableModelRowInserted(uiTableModel *m, int newIndex)
{
LVITEMW item;
int newCount;
newCount = uiprivTableModelNumRows(m);
ZeroMemory(&item, sizeof (LVITEMW));
item.mask = 0;
item.iItem = newIndex;
item.iSubItem = 0;
for (auto t : *(m->tables)) {
// actually insert the rows
if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) newCount, LVSICF_NOINVALIDATEALL) == 0)
logLastError(L"error calling LVM_SETITEMCOUNT in uiTableModelRowInserted()");
// and redraw every row from the new row down to simulate adding it
if (SendMessageW(t->hwnd, LVM_REDRAWITEMS, (WPARAM) newIndex, (LPARAM) (newCount - 1)) == FALSE)
logLastError(L"error calling LVM_REDRAWITEMS in uiTableModelRowInserted()");
// update selection state
if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1))
logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted() to update selection state");
}
}
// TODO compare LVM_UPDATE and LVM_REDRAWITEMS
void uiTableModelRowChanged(uiTableModel *m, int index)
{
for (auto t : *(m->tables))
if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) index, 0) == (LRESULT) (-1))
logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()");
}
// TODO document that when this is called, the model must return the OLD row count when asked
// TODO for this and the above, see what GTK+ requires and adjust accordingly
void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
{
int newCount;
newCount = uiprivTableModelNumRows(m);
newCount--;
for (auto t : *(m->tables)) {
// update selection state
if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1))
logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted() to update selection state");
// actually delete the rows
if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) newCount, LVSICF_NOINVALIDATEALL) == 0)
logLastError(L"error calling LVM_SETITEMCOUNT in uiTableModelRowDeleted()");
// and redraw every row from the new nth row down to simulate removing the old nth row
if (SendMessageW(t->hwnd, LVM_REDRAWITEMS, (WPARAM) oldIndex, (LPARAM) (newCount - 1)) == FALSE)
logLastError(L"error calling LVM_REDRAWITEMS in uiTableModelRowDeleted()");
}
}
uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m)
{
return m->mh;
}
// TODO explain all this
static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData)
{
uiTable *t = (uiTable *) dwRefData;
NMHDR *nmhdr = (NMHDR *) lParam;
bool finishEdit, abortEdit;
HWND header;
LRESULT lResult;
HRESULT hr;
finishEdit = false;
abortEdit = false;
switch (uMsg) {
case WM_TIMER:
if (wParam == (WPARAM) (&(t->inDoubleClickTimer))) {
t->inDoubleClickTimer = FALSE;
// TODO check errors
KillTimer(hwnd, wParam);
return 0;
}
if (wParam != (WPARAM) t)
break;
// TODO only increment and update if visible?
for (auto &i : *(t->indeterminatePositions)) {
i.second++;
// TODO check errors
SendMessageW(hwnd, LVM_UPDATE, (WPARAM) (i.first.first), 0);
}
return 0;
case WM_LBUTTONDOWN:
t->inLButtonDown = TRUE;
lResult = DefSubclassProc(hwnd, uMsg, wParam, lParam);
t->inLButtonDown = FALSE;
return lResult;
case WM_COMMAND:
if (HIWORD(wParam) == EN_UPDATE) {
// the real list view resizes the edit control on this notification specifically
hr = uiprivTableResizeWhileEditing(t);
if (hr != S_OK) {
// TODO
}
break;
}
// the real list view accepts changes in this case
if (HIWORD(wParam) == EN_KILLFOCUS)
finishEdit = true;
break; // don't override default handling
case WM_NOTIFY:
// list view accepts changes on column resize, but does not provide such notifications :/
header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0);
if (nmhdr->hwndFrom == header) {
NMHEADERW *nm = (NMHEADERW *) nmhdr;
switch (nmhdr->code) {
case HDN_ITEMCHANGED:
if ((nm->pitem->mask & HDI_WIDTH) == 0)
break;
// fall through
case HDN_DIVIDERDBLCLICK:
case HDN_TRACK:
case HDN_ENDTRACK:
finishEdit = true;
}
}
// I think this mirrors the WM_COMMAND one above... TODO
if (nmhdr->code == NM_KILLFOCUS)
finishEdit = true;
break; // don't override default handling
case LVM_CANCELEDITLABEL:
finishEdit = true;
// TODO properly imitate notifiactions
break; // don't override default handling
// TODO finish edit on WM_WINDOWPOSCHANGING and WM_SIZE?
// for the next three: this item is about to go away; don't bother keeping changes
case LVM_SETITEMCOUNT:
if (wParam <= t->editedItem)
abortEdit = true;
break; // don't override default handling
case LVM_DELETEITEM:
if (wParam == t->editedItem)
abortEdit = true;
break; // don't override default handling
case LVM_DELETEALLITEMS:
abortEdit = true;
break; // don't override default handling
case WM_NCDESTROY:
if (RemoveWindowSubclass(hwnd, tableSubProc, uIDSubclass) == FALSE)
logLastError(L"RemoveWindowSubclass()");
// fall through
}
if (finishEdit) {
hr = uiprivTableFinishEditingText(t);
if (hr != S_OK) {
// TODO
}
} else if (abortEdit) {
hr = uiprivTableAbortEditingText(t);
if (hr != S_OK) {
// TODO
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos)
{
uiTableValue *value;
int progress;
std::pair<int, int> p;
std::map<std::pair<int, int>, LONG>::iterator iter;
bool startTimer = false;
bool stopTimer = false;
value = uiprivTableModelCellValue(t->model, item, modelColumn);
progress = uiTableValueInt(value);
uiFreeTableValue(value);
p.first = item;
p.second = subitem;
iter = t->indeterminatePositions->find(p);
if (iter == t->indeterminatePositions->end()) {
if (progress == -1) {
startTimer = t->indeterminatePositions->size() == 0;
(*(t->indeterminatePositions))[p] = 0;
if (pos != NULL)
*pos = 0;
}
} else
if (progress != -1) {
t->indeterminatePositions->erase(p);
stopTimer = t->indeterminatePositions->size() == 0;
} else if (pos != NULL)
*pos = iter->second;
if (startTimer)
// the interval shown here is PBM_SETMARQUEE's default
// TODO should we pass a function here instead? it seems to be called by DispatchMessage(), not DefWindowProc(), but I'm still unsure
if (SetTimer(t->hwnd, (UINT_PTR) t, 30, NULL) == 0)
logLastError(L"SetTimer()");
if (stopTimer)
if (KillTimer(t->hwnd, (UINT_PTR) (&t)) == 0)
logLastError(L"KillTimer()");
return progress;
}
// TODO properly integrate compound statements
static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult)
{
uiTable *t = uiTable(c);
HRESULT hr;
switch (nmhdr->code) {
case LVN_GETDISPINFO:
hr = uiprivTableHandleLVN_GETDISPINFO(t, (NMLVDISPINFOW *) nmhdr, lResult);
if (hr != S_OK) {
// TODO
return FALSE;
}
return TRUE;
case NM_CUSTOMDRAW:
hr = uiprivTableHandleNM_CUSTOMDRAW(t, (NMLVCUSTOMDRAW *) nmhdr, lResult);
if (hr != S_OK) {
// TODO
return FALSE;
}
return TRUE;
case NM_CLICK:
#if 0
{
NMITEMACTIVATE *nm = (NMITEMACTIVATE *) nmhdr;
LVHITTESTINFO ht;
WCHAR buf[256];
ZeroMemory(&ht, sizeof (LVHITTESTINFO));
ht.pt = nm->ptAction;
if (SendMessageW(t->hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM) (&ht)) == (LRESULT) (-1))
MessageBoxW(GetAncestor(t->hwnd, GA_ROOT), L"No hit", L"No hit", MB_OK);
else {
wsprintf(buf, L"item %d subitem %d htflags 0x%I32X",
ht.iItem, ht.iSubItem, ht.flags);
MessageBoxW(GetAncestor(t->hwnd, GA_ROOT), buf, buf, MB_OK);
}
}
*lResult = 0;
return TRUE;
#else
hr = uiprivTableHandleNM_CLICK(t, (NMITEMACTIVATE *) nmhdr, lResult);
if (hr != S_OK) {
// TODO
return FALSE;
}
return TRUE;
#endif
case LVN_ITEMCHANGED:
{
NMLISTVIEW *nm = (NMLISTVIEW *) nmhdr;
UINT oldSelected, newSelected;
HRESULT hr;
// TODO clean up these if cases
if (!t->inLButtonDown && t->edit == NULL)
return FALSE;
oldSelected = nm->uOldState & LVIS_SELECTED;
newSelected = nm->uNewState & LVIS_SELECTED;
if (t->inLButtonDown && oldSelected == 0 && newSelected != 0) {
t->inDoubleClickTimer = TRUE;
// TODO check error
SetTimer(t->hwnd, (UINT_PTR) (&(t->inDoubleClickTimer)),
GetDoubleClickTime(), NULL);
*lResult = 0;
return TRUE;
}
// the nm->iItem == -1 case is because that is used if "the change has been applied to all items in the list view"
if (t->edit != NULL && oldSelected != 0 && newSelected == 0 && (t->editedItem == nm->iItem || nm->iItem == -1)) {
// TODO see if the real list view accepts or rejects changes here; Windows Explorer accepts
hr = uiprivTableFinishEditingText(t);
if (hr != S_OK) {
// TODO
return FALSE;
}
*lResult = 0;
return TRUE;
}
return FALSE;
}
// the real list view accepts changes when scrolling or clicking column headers
case LVN_BEGINSCROLL:
case LVN_COLUMNCLICK:
hr = uiprivTableFinishEditingText(t);
if (hr != S_OK) {
// TODO
return FALSE;
}
*lResult = 0;
return TRUE;
}
return FALSE;
}
static void uiTableDestroy(uiControl *c)
{
uiTable *t = uiTable(c);
uiTableModel *model = t->model;
std::vector<uiTable *>::iterator it;
HRESULT hr;
hr = uiprivTableAbortEditingText(t);
if (hr != S_OK) {
// TODO
}
uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd);
uiWindowsEnsureDestroyWindow(t->hwnd);
// detach table from model
for (it = model->tables->begin(); it != model->tables->end(); it++) {
if (*it == t) {
model->tables->erase(it);
break;
}
}
// free the columns
for (auto col : *(t->columns))
uiprivFree(col);
delete t->columns;
// t->imagelist will be automatically destroyed
delete t->indeterminatePositions;
uiFreeControl(uiControl(t));
}
uiWindowsControlAllDefaultsExceptDestroy(uiTable)
// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing:
// "columns widths that avoid truncated data x an integral number of items"
// Don't think that'll cut it when some cells have overlong data (eg
// stupidly long URLs). So for now, just hardcode a minimum.
// TODO Investigate using LVM_GETHEADER/HDM_LAYOUT here
// TODO investigate using LVM_APPROXIMATEVIEWRECT here
#define tableMinWidth 107 /* in line with other controls */
#define tableMinHeight (14 * 3) /* header + 2 lines (roughly) */
static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height)
{
uiTable *t = uiTable(c);
uiWindowsSizing sizing;
int x, y;
x = tableMinWidth;
y = tableMinHeight;
uiWindowsGetSizing(t->hwnd, &sizing);
uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y);
*width = x;
*height = y;
}
static uiprivTableColumnParams *appendColumn(uiTable *t, const char *name, int colfmt)
{
WCHAR *wstr;
LVCOLUMNW lvc;
uiprivTableColumnParams *p;
ZeroMemory(&lvc, sizeof (LVCOLUMNW));
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
lvc.fmt = colfmt;
lvc.cx = 120; // TODO
wstr = toUTF16(name);
lvc.pszText = wstr;
if (SendMessageW(t->hwnd, LVM_INSERTCOLUMNW, t->nColumns, (LPARAM) (&lvc)) == (LRESULT) (-1))
logLastError(L"error calling LVM_INSERTCOLUMNW in appendColumn()");
uiprivFree(wstr);
t->nColumns++;
p = uiprivNew(uiprivTableColumnParams);
p->textModelColumn = -1;
p->textEditableModelColumn = -1;
p->textParams = uiprivDefaultTextColumnOptionalParams;
p->imageModelColumn = -1;
p->checkboxModelColumn = -1;
p->checkboxEditableModelColumn = -1;
p->progressBarModelColumn = -1;
p->buttonModelColumn = -1;
t->columns->push_back(p);
return p;
}
void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
uiprivTableColumnParams *p;
p = appendColumn(t, name, LVCFMT_LEFT);
p->textModelColumn = textModelColumn;
p->textEditableModelColumn = textEditableModelColumn;
if (textParams != NULL)
p->textParams = *textParams;
}
void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn)
{
uiprivTableColumnParams *p;
p = appendColumn(t, name, LVCFMT_LEFT);
p->imageModelColumn = imageModelColumn;
}
void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
uiprivTableColumnParams *p;
p = appendColumn(t, name, LVCFMT_LEFT);
p->textModelColumn = textModelColumn;
p->textEditableModelColumn = textEditableModelColumn;
if (textParams != NULL)
p->textParams = *textParams;
p->imageModelColumn = imageModelColumn;
}
void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn)
{
uiprivTableColumnParams *p;
p = appendColumn(t, name, LVCFMT_LEFT);
p->checkboxModelColumn = checkboxModelColumn;
p->checkboxEditableModelColumn = checkboxEditableModelColumn;
}
void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)
{
uiprivTableColumnParams *p;
p = appendColumn(t, name, LVCFMT_LEFT);
p->textModelColumn = textModelColumn;
p->textEditableModelColumn = textEditableModelColumn;
if (textParams != NULL)
p->textParams = *textParams;
p->checkboxModelColumn = checkboxModelColumn;
p->checkboxEditableModelColumn = checkboxEditableModelColumn;
}
void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn)
{
uiprivTableColumnParams *p;
p = appendColumn(t, name, LVCFMT_LEFT);
p->progressBarModelColumn = progressModelColumn;
}
void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn)
{
uiprivTableColumnParams *p;
// TODO see if we can get rid of this parameter
p = appendColumn(t, name, LVCFMT_LEFT);
p->buttonModelColumn = buttonModelColumn;
p->buttonClickableModelColumn = buttonClickableModelColumn;
}
uiTable *uiNewTable(uiTableParams *p)
{
uiTable *t;
int n;
HRESULT hr;
uiWindowsNewControl(uiTable, t);
t->columns = new std::vector<uiprivTableColumnParams *>;
t->model = p->Model;
t->backgroundColumn = p->RowBackgroundColorModelColumn;
// WS_CLIPCHILDREN is here to prevent drawing over the edit box used for editing text
t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE,
WC_LISTVIEW, L"",
LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_CLIPCHILDREN | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL,
hInstance, NULL,
TRUE);
t->model->tables->push_back(t);
uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t));
// TODO: try LVS_EX_AUTOSIZECOLUMNS
// TODO check error
SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE,
(WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES),
(LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES));
n = uiprivTableModelNumRows(t->model);
if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0)
logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()");
hr = uiprivUpdateImageListSize(t);
if (hr != S_OK) {
// TODO
}
t->indeterminatePositions = new std::map<std::pair<int, int>, LONG>;
if (SetWindowSubclass(t->hwnd, tableSubProc, 0, (DWORD_PTR) t) == FALSE)
logLastError(L"SetWindowSubclass()");
return t;
}

81
windows/table.hpp Normal file
View File

@ -0,0 +1,81 @@
// 10 june 2018
#include "../common/table.h"
// table.cpp
#define uiprivNumLVN_GETDISPINFOSkip 3
struct uiTableModel {
uiTableModelHandler *mh;
std::vector<uiTable *> *tables;
};
typedef struct uiprivTableColumnParams uiprivTableColumnParams;
struct uiprivTableColumnParams {
int textModelColumn;
int textEditableModelColumn;
uiTableTextColumnOptionalParams textParams;
int imageModelColumn;
int checkboxModelColumn;
int checkboxEditableModelColumn;
int progressBarModelColumn;
int buttonModelColumn;
int buttonClickableModelColumn;
};
struct uiTable {
uiWindowsControl c;
uiTableModel *model;
HWND hwnd;
std::vector<uiprivTableColumnParams *> *columns;
WPARAM nColumns;
int backgroundColumn;
// TODO make sure replacing images while selected in the listview is even allowed
HIMAGELIST imagelist;
// TODO document all this
std::map<std::pair<int, int>, LONG> *indeterminatePositions;
BOOL inLButtonDown;
// TODO is this even necessary? it seems NM_CLICK is not sent if NM_DBLCLICK or LVN_ITEMACTIVATE (one of the two) happens...
BOOL inDoubleClickTimer;
HWND edit;
int editedItem;
int editedSubitem;
};
extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos);
// tabledispinfo.cpp
extern HRESULT uiprivTableHandleLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm, LRESULT *lResult);
// tabledraw.cpp
extern HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult);
extern HRESULT uiprivUpdateImageListSize(uiTable *t);
// tableediting.cpp
extern HRESULT uiprivTableResizeWhileEditing(uiTable *t);
extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult);
extern HRESULT uiprivTableFinishEditingText(uiTable *t);
extern HRESULT uiprivTableAbortEditingText(uiTable *t);
// tablemetrics.cpp
typedef struct uiprivTableMetrics uiprivTableMetrics;
struct uiprivTableMetrics {
BOOL hasText;
BOOL hasImage;
BOOL focused;
BOOL selected;
RECT itemBounds;
RECT itemIcon;
RECT itemLabel;
RECT subitemBounds;
RECT subitemIcon;
RECT subitemLabel;
LRESULT bitmapMargin;
int cxIcon;
int cyIcon;
RECT realTextBackground;
RECT realTextRect;
};
extern HRESULT uiprivTableGetMetrics(uiTable *t, int iItem, int iSubItem, uiprivTableMetrics **mout);

99
windows/tabledispinfo.cpp Normal file
View File

@ -0,0 +1,99 @@
// 13 june 2018
#include "uipriv_windows.hpp"
#include "table.hpp"
// further reading:
// - https://msdn.microsoft.com/en-us/library/ye4z8x58.aspx
static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p)
{
int strcol;
uiTableValue *value;
WCHAR *wstr;
int progress;
HRESULT hr;
if ((nm->item.mask & LVIF_TEXT) == 0)
return S_OK;
strcol = -1;
if (p->textModelColumn != -1)
strcol = p->textModelColumn;
else if (p->buttonModelColumn != -1)
strcol = p->buttonModelColumn;
if (strcol != -1) {
value = uiprivTableModelCellValue(t->model, nm->item.iItem, strcol);
wstr = toUTF16(uiTableValueString(value));
uiFreeTableValue(value);
// We *could* just make pszText into a freshly allocated
// conversion and avoid the limitation of cchTextMax.
// But then, we would have to keep things around for some
// amount of time (some pages on MSDN say 2 additional
// LVN_GETDISPINFO messages). And in practice, anything
// that results in extra LVN_GETDISPINFO messages (such
// as LVN_GETITEMRECT with LVIR_LABEL) will break this
// counting.
// TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end)
wcsncpy(nm->item.pszText, wstr, nm->item.cchTextMax);
nm->item.pszText[nm->item.cchTextMax - 1] = L'\0';
uiprivFree(wstr);
return S_OK;
}
if (p->progressBarModelColumn != -1) {
progress = uiprivTableProgress(t, nm->item.iItem, nm->item.iSubItem, p->progressBarModelColumn, NULL);
if (progress == -1) {
// TODO either localize this or replace it with something that's language-neutral
// TODO ensure null terminator
wcsncpy(nm->item.pszText, L"Indeterminate", nm->item.cchTextMax);
return S_OK;
}
// TODO ensure null terminator
_snwprintf(nm->item.pszText, nm->item.cchTextMax, L"%d%%", progress);
return S_OK;
}
return S_OK;
}
static HRESULT handleLVIF_IMAGE(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p)
{
uiTableValue *value;
HRESULT hr;
if (nm->item.iSubItem == 0 && p->imageModelColumn == -1 && p->checkboxModelColumn == -1) {
// Having an image list always leaves space for an image
// on the main item :|
// Other places on the internet imply that you should be
// able to do this but that it shouldn't work, but it works
// perfectly (and pixel-perfectly too) for me, so...
nm->item.mask |= LVIF_INDENT;
nm->item.iIndent = -1;
}
if ((nm->item.mask & LVIF_IMAGE) == 0)
return S_OK; // nothing to do here
// TODO see if the -1 part is correct
// TODO see if we should use state instead of images for checkbox value
nm->item.iImage = -1;
if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1)
nm->item.iImage = 0;
return S_OK;
}
HRESULT uiprivTableHandleLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm, LRESULT *lResult)
{
uiprivTableColumnParams *p;
HRESULT hr;
p = (*(t->columns))[nm->item.iSubItem];
hr = handleLVIF_TEXT(t, nm, p);
if (hr != S_OK)
return hr;
hr = handleLVIF_IMAGE(t, nm, p);
if (hr != S_OK)
return hr;
*lResult = 0;
return S_OK;
}

739
windows/tabledraw.cpp Normal file
View File

@ -0,0 +1,739 @@
// 14 june 2018
#include "uipriv_windows.hpp"
#include "table.hpp"
// TODOs:
// - properly hide selection when not focused (or switch on LVS_SHOWSELALWAYS and draw that state)
// TODO maybe split this into item and subitem structs?
struct drawState {
uiTable *t;
uiTableModel *model;
uiprivTableColumnParams *p;
HDC dc;
int iItem;
int iSubItem;
uiprivTableMetrics *m;
COLORREF bgColor;
HBRUSH bgBrush;
BOOL freeBgBrush;
COLORREF textColor;
HBRUSH textBrush;
BOOL freeTextBrush;
};
static HRESULT drawBackgrounds(HRESULT hr, struct drawState *s)
{
if (hr != S_OK)
return hr;
if (s->m->hasImage)
if (FillRect(s->dc, &(s->m->subitemIcon), GetSysColorBrush(COLOR_WINDOW)) == 0) {
logLastError(L"FillRect() icon");
return E_FAIL;
}
if (FillRect(s->dc, &(s->m->realTextBackground), s->bgBrush) == 0) {
logLastError(L"FillRect()");
return E_FAIL;
}
return S_OK;
}
static void centerImageRect(RECT *image, RECT *space)
{
LONG xoff, yoff;
// first make sure both have the same upper-left
xoff = image->left - space->left;
yoff = image->top - space->top;
image->left -= xoff;
image->top -= yoff;
image->right -= xoff;
image->bottom -= yoff;
// now center
xoff = ((space->right - space->left) - (image->right - image->left)) / 2;
yoff = ((space->bottom - space->top) - (image->bottom - image->top)) / 2;
image->left += xoff;
image->top += yoff;
image->right += xoff;
image->bottom += yoff;
}
static HRESULT drawImagePart(HRESULT hr, struct drawState *s)
{
uiTableValue *value;
IWICBitmap *wb;
HBITMAP b;
RECT r;
UINT fStyle;
if (hr != S_OK)
return hr;
if (s->p->imageModelColumn == -1)
return S_OK;
value = uiprivTableModelCellValue(s->model, s->iItem, s->p->imageModelColumn);
wb = uiprivImageAppropriateForDC(uiTableValueImage(value), s->dc);
uiFreeTableValue(value);
hr = uiprivWICToGDI(wb, s->dc, s->m->cxIcon, s->m->cyIcon, &b);
if (hr != S_OK)
return hr;
// TODO rewrite this condition to make more sense; possibly swap the if and else blocks too
// TODO proper cleanup
if (ImageList_GetImageCount(s->t->imagelist) > 1) {
if (ImageList_Replace(s->t->imagelist, 0, b, NULL) == 0) {
logLastError(L"ImageList_Replace()");
return E_FAIL;
}
} else
if (ImageList_Add(s->t->imagelist, b, NULL) == -1) {
logLastError(L"ImageList_Add()");
return E_FAIL;
}
// TODO error check
DeleteObject(b);
r = s->m->subitemIcon;
r.right = r.left + s->m->cxIcon;
r.bottom = r.top + s->m->cyIcon;
centerImageRect(&r, &(s->m->subitemIcon));
fStyle = ILD_NORMAL;
if (s->m->selected)
fStyle = ILD_SELECTED;
if (ImageList_Draw(s->t->imagelist, 0,
s->dc, r.left, r.top, fStyle) == 0) {
logLastError(L"ImageList_Draw()");
return E_FAIL;
}
return S_OK;
}
// references for checkbox drawing:
// - https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485
// - https://blogs.msdn.microsoft.com/oldnewthing/20171201-00/?p=97505
static HRESULT drawUnthemedCheckbox(struct drawState *s, int checked, int enabled)
{
RECT r;
UINT state;
r = s->m->subitemIcon;
// this is what the actual list view LVS_EX_CHECKBOXES code does to size the checkboxes
// TODO reverify the initial size
r.right = r.left + GetSystemMetrics(SM_CXSMICON);
r.bottom = r.top + GetSystemMetrics(SM_CYSMICON);
if (InflateRect(&r, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE)) == 0) {
logLastError(L"InflateRect()");
return E_FAIL;
}
r.right++;
r.bottom++;
centerImageRect(&r, &(s->m->subitemIcon));
state = DFCS_BUTTONCHECK | DFCS_FLAT;
if (checked)
state |= DFCS_CHECKED;
if (!enabled)
state |= DFCS_INACTIVE;
if (DrawFrameControl(s->dc, &r, DFC_BUTTON, state) == 0) {
logLastError(L"DrawFrameControl()");
return E_FAIL;
}
return S_OK;
}
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;
}
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;
}
static HRESULT drawTextPart(HRESULT hr, struct drawState *s)
{
COLORREF prevText;
int prevMode;
RECT r;
uiTableValue *value;
WCHAR *wstr;
if (hr != S_OK)
return hr;
if (!s->m->hasText)
return S_OK;
// don't draw the text underneath an edit control
if (s->t->edit != NULL && s->t->editedItem == s->iItem && s->t->editedSubitem == s->iSubItem)
return S_OK;
prevText = SetTextColor(s->dc, s->textColor);
if (prevText == CLR_INVALID) {
logLastError(L"SetTextColor()");
return E_FAIL;
}
prevMode = SetBkMode(s->dc, TRANSPARENT);
if (prevMode == 0) {
logLastError(L"SetBkMode()");
return E_FAIL;
}
value = uiprivTableModelCellValue(s->model, s->iItem, s->p->textModelColumn);
wstr = toUTF16(uiTableValueString(value));
uiFreeTableValue(value);
// These flags are a menagerie of flags from various sources:
// guessing, the Windows 2000 source leak, various custom
// draw examples on the web, etc.
// TODO find the real correct flags
if (DrawTextW(s->dc, wstr, -1, &(s->m->realTextRect), DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) {
uiprivFree(wstr);
logLastError(L"DrawTextW()");
return E_FAIL;
}
uiprivFree(wstr);
// TODO decide once and for all what to compare to here and with SelectObject()
if (SetBkMode(s->dc, prevMode) != TRANSPARENT) {
logLastError(L"SetBkMode() prev");
return E_FAIL;
}
if (SetTextColor(s->dc, prevText) != s->textColor) {
logLastError(L"SetTextColor() prev");
return E_FAIL;
}
return S_OK;
}
// much of this is to imitate what shell32.dll's CDrawProgressBar does
#define indeterminateSegments 8
static HRESULT drawProgressBarPart(HRESULT hr, struct drawState *s)
{
int progress;
LONG indeterminatePos;
HTHEME theme;
RECT r;
RECT rBorder, rFill[2];
int i, nFill;
TEXTMETRICW tm;
int sysColor;
if (hr != S_OK)
return hr;
if (s->p->progressBarModelColumn == -1)
return S_OK;
progress = uiprivTableProgress(s->t, s->iItem, s->iSubItem, s->p->progressBarModelColumn, &indeterminatePos);
theme = OpenThemeData(s->t->hwnd, L"PROGRESS");
if (GetTextMetricsW(s->dc, &tm) == 0) {
logLastError(L"GetTextMetricsW()");
hr = E_FAIL;
goto fail;
}
r = s->m->subitemBounds;
// this sets the height of the progressbar and vertically centers it in one fell swoop
r.top += (r.bottom - tm.tmHeight - r.top) / 2;
r.bottom = r.top + tm.tmHeight;
// TODO check errors
rBorder = r;
InflateRect(&rBorder, -1, -1);
if (theme != NULL) {
RECT crect;
hr = GetThemeBackgroundContentRect(theme, s->dc,
PP_TRANSPARENTBAR, PBBS_NORMAL,
&rBorder, &crect);
if (hr != S_OK) {
logHRESULT(L"GetThemeBackgroundContentRect()", hr);
goto fail;
}
hr = DrawThemeBackground(theme, s->dc,
PP_TRANSPARENTBAR, PBBS_NORMAL,
&crect, NULL);
if (hr != S_OK) {
logHRESULT(L"DrawThemeBackground() border", hr);
goto fail;
}
} else {
HPEN pen, prevPen;
HBRUSH brush, prevBrush;
sysColor = COLOR_HIGHLIGHT;
if (s->m->selected)
sysColor = COLOR_HIGHLIGHTTEXT;
// TODO check errors everywhere
pen = CreatePen(PS_SOLID, 1, GetSysColor(sysColor));
prevPen = (HPEN) SelectObject(s->dc, pen);
brush = (HBRUSH) GetStockObject(NULL_BRUSH);
prevBrush = (HBRUSH) SelectObject(s->dc, brush);
Rectangle(s->dc, rBorder.left, rBorder.top, rBorder.right, rBorder.bottom);
SelectObject(s->dc, prevBrush);
SelectObject(s->dc, prevPen);
DeleteObject(pen);
}
nFill = 1;
rFill[0] = r;
// TODO check error
InflateRect(&rFill[0], -1, -1);
if (progress != -1)
rFill[0].right -= (rFill[0].right - rFill[0].left) * (100 - progress) / 100;
else {
LONG barWidth;
LONG pieceWidth;
// TODO explain all this
// TODO this should really start the progressbar scrolling into view instead of already on screen when first set
rFill[1] = rFill[0]; // save in case we need it
barWidth = rFill[0].right - rFill[0].left;
pieceWidth = barWidth / indeterminateSegments;
rFill[0].left += indeterminatePos % barWidth;
if ((rFill[0].left + pieceWidth) >= rFill[0].right) {
// make this piece wrap back around
nFill++;
rFill[1].right = rFill[1].left + (pieceWidth - (rFill[0].right - rFill[0].left));
} else
rFill[0].right = rFill[0].left + pieceWidth;
}
for (i = 0; i < nFill; i++)
if (theme != NULL) {
hr = DrawThemeBackground(theme, s->dc,
PP_FILL, PBFS_NORMAL,
&rFill[i], NULL);
if (hr != S_OK) {
logHRESULT(L"DrawThemeBackground() fill", hr);
goto fail;
}
} else
// TODO check errors
FillRect(s->dc, &rFill[i], GetSysColorBrush(sysColor));
hr = S_OK;
fail:
// TODO check errors
if (theme != NULL)
CloseThemeData(theme);
return hr;
}
static HRESULT drawButtonPart(HRESULT hr, struct drawState *s)
{
uiTableValue *value;
WCHAR *wstr;
bool enabled;
HTHEME theme;
RECT r;
TEXTMETRICW tm;
if (hr != S_OK)
return hr;
if (s->p->buttonModelColumn == -1)
return S_OK;
value = uiprivTableModelCellValue(s->model, s->iItem, s->p->buttonModelColumn);
wstr = toUTF16(uiTableValueString(value));
uiFreeTableValue(value);
enabled = uiprivTableModelCellEditable(s->model, s->iItem, s->p->buttonClickableModelColumn);
theme = OpenThemeData(s->t->hwnd, L"button");
if (GetTextMetricsW(s->dc, &tm) == 0) {
logLastError(L"GetTextMetricsW()");
hr = E_FAIL;
goto fail;
}
r = s->m->subitemBounds;
if (theme != NULL) {
int state;
state = PBS_NORMAL;
if (!enabled)
state = PBS_DISABLED;
hr = DrawThemeBackground(theme, s->dc,
BP_PUSHBUTTON, state,
&r, NULL);
if (hr != S_OK) {
logHRESULT(L"DrawThemeBackground()", hr);
goto fail;
}
// TODO DT_EDITCONTROL?
// TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here
// TODO is there a theme property we can get instead of hardcoding these flags? if not, make these flags a macro
hr = DrawThemeText(theme, s->dc,
BP_PUSHBUTTON, state,
wstr, -1,
DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX, 0,
&r);
if (hr != S_OK) {
logHRESULT(L"DrawThemeText()", hr);
goto fail;
}
} else {
UINT state;
HBRUSH color, prevColor;
int prevBkMode;
// TODO check errors
// TODO explain why we're not doing this in the themed case (it has to do with extra transparent pixels)
InflateRect(&r, -1, -1);
state = DFCS_BUTTONPUSH;
if (!enabled)
state |= DFCS_INACTIVE;
if (DrawFrameControl(s->dc, &r, DFC_BUTTON, state) == 0) {
logLastError(L"DrawFrameControl()");
hr = E_FAIL;
goto fail;
}
color = GetSysColorBrush(COLOR_BTNTEXT);
// TODO check errors for these two
prevColor = (HBRUSH) SelectObject(s->dc, color);
prevBkMode = SetBkMode(s->dc, TRANSPARENT);
// TODO DT_EDITCONTROL?
// TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here
if (DrawTextW(s->dc, wstr, -1, &r, DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX) == 0) {
logLastError(L"DrawTextW()");
hr = E_FAIL;
goto fail;
}
// TODO check errors for these two
SetBkMode(s->dc, prevBkMode);
SelectObject(s->dc, prevColor);
}
hr = S_OK;
fail:
// TODO check errors
if (theme != NULL)
CloseThemeData(theme);
uiprivFree(wstr);
return hr;
}
static HRESULT freeDrawState(struct drawState *s)
{
HRESULT hr, hrret;
hrret = S_OK;
if (s->m != NULL) {
uiprivFree(s->m);
s->m = NULL;
}
if (s->freeTextBrush) {
if (DeleteObject(s->textBrush) == 0) {
logLastError(L"DeleteObject()");
hrret = E_FAIL;
// continue cleaning up anyway
}
s->freeTextBrush = FALSE;
}
if (s->freeBgBrush) {
if (DeleteObject(s->bgBrush) == 0) {
logLastError(L"DeleteObject()");
hrret = E_FAIL;
// continue cleaning up anyway
}
s->freeBgBrush = FALSE;
}
return hrret;
}
static COLORREF blend(COLORREF base, double r, double g, double b, double a)
{
double br, bg, bb;
br = ((double) GetRValue(base)) / 255.0;
bg = ((double) GetGValue(base)) / 255.0;
bb = ((double) GetBValue(base)) / 255.0;
br = (r * a) + (br * (1.0 - a));
bg = (g * a) + (bg * (1.0 - a));
bb = (b * a) + (bb * (1.0 - a));
return RGB((BYTE) (br * 255),
(BYTE) (bg * 255),
(BYTE) (bb * 255));
}
static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p)
{
LRESULT state;
HWND header;
HRESULT hr;
ZeroMemory(s, sizeof (struct drawState));
s->t = t;
s->model = t->model;
s->p = p;
s->dc = nm->nmcd.hdc;
s->iItem = nm->nmcd.dwItemSpec;
s->iSubItem = nm->iSubItem;
hr = uiprivTableGetMetrics(t, s->iItem, s->iSubItem, &(s->m));
if (hr != S_OK)
goto fail;
if (s->m->selected) {
s->bgColor = GetSysColor(COLOR_HIGHLIGHT);
s->bgBrush = GetSysColorBrush(COLOR_HIGHLIGHT);
s->textColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
s->textBrush = GetSysColorBrush(COLOR_HIGHLIGHTTEXT);
} else {
double r, g, b, a;
s->bgColor = GetSysColor(COLOR_WINDOW);
s->bgBrush = GetSysColorBrush(COLOR_WINDOW);
if (uiprivTableModelColorIfProvided(s->model, s->iItem, t->backgroundColumn, &r, &g, &b, &a)) {
s->bgColor = blend(s->bgColor, r, g, b, a);
s->bgBrush = CreateSolidBrush(s->bgColor);
if (s->bgBrush == NULL) {
logLastError(L"CreateSolidBrush()");
hr = E_FAIL;
goto fail;
}
s->freeBgBrush = TRUE;
}
s->textColor = GetSysColor(COLOR_WINDOWTEXT);
s->textBrush = GetSysColorBrush(COLOR_WINDOWTEXT);
if (uiprivTableModelColorIfProvided(s->model, s->iItem, p->textParams.ColorModelColumn, &r, &g, &b, &a)) {
s->textColor = blend(s->bgColor, r, g, b, a);
s->textBrush = CreateSolidBrush(s->textColor);
if (s->textBrush == NULL) {
logLastError(L"CreateSolidBrush()");
hr = E_FAIL;
goto fail;
}
s->freeTextBrush = TRUE;
}
}
return S_OK;
fail:
// ignore the error; we need to return the one we got above
freeDrawState(s);
return hr;
}
static HRESULT updateAndDrawFocusRects(HRESULT hr, uiTable *t, HDC dc, int iItem, RECT *realTextBackground, RECT *focus, bool *first)
{
LRESULT state;
if (hr != S_OK)
return hr;
if (GetFocus() != t->hwnd)
return S_OK;
// uItemState CDIS_FOCUS doesn't quite work right because of bugs in the Windows list view that causes spurious redraws without the flag while we hover over the focused item
// TODO only call this once
state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, (WPARAM) iItem, (LRESULT) (LVIS_FOCUSED));
if ((state & LVIS_FOCUSED) == 0)
return S_OK;
if (realTextBackground != NULL)
if (*first) {
*focus = *realTextBackground;
*first = false;
return S_OK;
} else if (focus->right == realTextBackground->left) {
focus->right = realTextBackground->right;
return S_OK;
}
if (DrawFocusRect(dc, focus) == 0) {
logLastError(L"DrawFocusRect()");
return E_FAIL;
}
if (realTextBackground != NULL)
*focus = *realTextBackground;
return S_OK;
}
// normally we would only draw stuff in subitem stages
// this broke when we tried drawing focus rects in postpaint; the drawing either kept getting wiped or overdrawn, mouse hovering had something to do with it, and none of the "solutions" to this or similar problems on the internet worked
// so now we do everything in the item prepaint stage
// TODO consider switching to full-on owner draw
// TODO only compute the background brushes once?
HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult)
{
struct drawState s;
uiprivTableColumnParams *p;
NMLVCUSTOMDRAW b;
size_t i, n;
RECT focus;
bool focusFirst;
HRESULT hr;
switch (nm->nmcd.dwDrawStage) {
case CDDS_PREPAINT:
*lResult = CDRF_NOTIFYITEMDRAW;
return S_OK;
case CDDS_ITEMPREPAINT:
break;
default:
*lResult = CDRF_DODEFAULT;
return S_OK;
}
n = t->columns->size();
b = *nm;
focusFirst = true;
for (i = 0; i < n; i++) {
b.iSubItem = i;
p = (*(t->columns))[i];
hr = fillDrawState(&s, t, &b, p);
if (hr != S_OK)
return hr;
hr = drawBackgrounds(S_OK, &s);
hr = drawImagePart(hr, &s);
hr = drawCheckboxPart(hr, &s);
hr = drawTextPart(hr, &s);
hr = drawProgressBarPart(hr, &s);
hr = drawButtonPart(hr, &s);
hr = updateAndDrawFocusRects(hr, s.t, s.dc, nm->nmcd.dwItemSpec, &(s.m->realTextBackground), &focus, &focusFirst);
if (hr != S_OK)
goto fail;
hr = freeDrawState(&s);
if (hr != S_OK) // TODO really error out here?
return hr;
}
// and draw the last focus rect
hr = updateAndDrawFocusRects(hr, t, nm->nmcd.hdc, nm->nmcd.dwItemSpec, NULL, &focus, &focusFirst);
if (hr != S_OK)
return hr;
*lResult = CDRF_SKIPDEFAULT;
return S_OK;
fail:
// ignore error here
// TODO this is awkward cleanup placement for something that only really exists in a for loop
freeDrawState(&s);
return hr;
}
// 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;
}

262
windows/tableediting.cpp Normal file
View File

@ -0,0 +1,262 @@
// 17 june 2018
#include "uipriv_windows.hpp"
#include "table.hpp"
// TODOs
// - clicking on the same item restarts editing instead of cancels it
// this is not how the real list view positions and sizes the edit control, but this is a) close enough b) a lot easier to follow c) something I can actually get working d) something I'm slightly more comfortable including in libui
static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, int iItem, int iSubItem)
{
uiprivTableMetrics *m;
RECT r;
HDC dc;
HFONT prevFont;
TEXTMETRICW tm;
SIZE textSize;
RECT editRect, clientRect;
HRESULT hr;
hr = uiprivTableGetMetrics(t, iItem, iSubItem, &m);
if (hr != S_OK)
return hr;
r = m->realTextRect;
uiprivFree(m);
// TODO check errors for all these
dc = GetDC(t->hwnd); // use the list view DC since we're using its coordinate space
prevFont = (HFONT) SelectObject(dc, hMessageFont);
GetTextMetricsW(dc, &tm);
GetTextExtentPoint32W(dc, wstr, wcslen(wstr), &textSize);
SelectObject(dc, prevFont);
ReleaseDC(t->hwnd, dc);
SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&editRect));
r.left -= editRect.left;
// find the top of the text
r.top += ((r.bottom - r.top) - tm.tmHeight) / 2;
// and move THAT by the right offset
r.top -= editRect.top;
r.right = r.left + textSize.cx;
// the real listview does this to add some extra space at the end
// TODO this still isn't enough space
r.right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE);
// and make the bottom equally positioned to the top
r.bottom = r.top + editRect.top + tm.tmHeight + editRect.top;
// make sure the edit box doesn't stretch outside the listview
// the list view just does this, which is dumb for when the list view wouldn't be visible at all, but given that it doesn't scroll the edit into view either...
// TODO check errors
GetClientRect(t->hwnd, &clientRect);
IntersectRect(&r, &r, &clientRect);
// TODO check error or use the right function
SetWindowPos(t->edit, NULL,
r.left, r.top,
r.right - r.left, r.bottom - r.top,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
return S_OK;
}
// the real list view intercepts these keys to control editing
static LRESULT CALLBACK editSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData)
{
uiTable *t = (uiTable *) dwRefData;
HRESULT hr;
switch (uMsg) {
case WM_KEYDOWN:
switch (wParam) {
// TODO handle VK_TAB and VK_SHIFT+VK_TAB
case VK_RETURN:
hr = uiprivTableFinishEditingText(t);
if (hr != S_OK) {
// TODO
}
return 0; // yes, the real list view just returns here
case VK_ESCAPE:
hr = uiprivTableAbortEditingText(t);
if (hr != S_OK) {
// TODO
}
return 0;
}
break;
// the real list view also forces these flags
case WM_GETDLGCODE:
return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
case WM_NCDESTROY:
if (RemoveWindowSubclass(hwnd, editSubProc, uIDSubclass) == FALSE)
logLastError(L"RemoveWindowSubclass()");
// fall through
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableColumnParams *p)
{
uiTableValue *value;
WCHAR *wstr;
HRESULT hr;
// the real list view accepts changes to the existing item when editing a new item
hr = uiprivTableFinishEditingText(t);
if (hr != S_OK)
return hr;
// the real list view creates the edit control with the string
value = uiprivTableModelCellValue(t->model, iItem, p->textModelColumn);
wstr = toUTF16(uiTableValueString(value));
uiFreeTableValue(value);
// TODO copy WS_EX_RTLREADING
t->edit = CreateWindowExW(0,
L"EDIT", wstr,
// these styles are what the normal listview edit uses
WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER | ES_AUTOHSCROLL,
// as is this size
0, 0, 16384, 16384,
// and this control ID
t->hwnd, (HMENU) 1, hInstance, NULL);
if (t->edit == NULL) {
logLastError(L"CreateWindowExW()");
uiprivFree(wstr);
return E_FAIL;
}
SendMessageW(t->edit, WM_SETFONT, (WPARAM) hMessageFont, (LPARAM) TRUE);
// TODO check errors
SetWindowSubclass(t->edit, editSubProc, 0, (DWORD_PTR) t);
hr = resizeEdit(t, wstr, iItem, iSubItem);
if (hr != S_OK)
// TODO proper cleanup
return hr;
// TODO check errors on these two, if any
SetFocus(t->edit);
ShowWindow(t->edit, SW_SHOW);
SendMessageW(t->edit, EM_SETSEL, 0, (LPARAM) (-1));
uiprivFree(wstr);
t->editedItem = iItem;
t->editedSubitem = iSubItem;
return S_OK;
}
HRESULT uiprivTableResizeWhileEditing(uiTable *t)
{
WCHAR *text;
HRESULT hr;
if (t->edit == NULL)
return S_OK;
text = windowText(t->edit);
hr = resizeEdit(t, text, t->editedItem, t->editedSubitem);
uiprivFree(text);
return hr;
}
HRESULT uiprivTableFinishEditingText(uiTable *t)
{
uiprivTableColumnParams *p;
uiTableValue *value;
char *text;
if (t->edit == NULL)
return S_OK;
text = uiWindowsWindowText(t->edit);
value = uiNewTableValueString(text);
uiFreeText(text);
p = (*(t->columns))[t->editedSubitem];
uiprivTableModelSetCellValue(t->model, t->editedItem, p->textModelColumn, value);
uiFreeTableValue(value);
// always refresh the value in case the model rejected it
if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (t->editedItem), 0) == (LRESULT) (-1)) {
logLastError(L"LVM_UPDATE");
return E_FAIL;
}
return uiprivTableAbortEditingText(t);
}
HRESULT uiprivTableAbortEditingText(uiTable *t)
{
HWND edit;
if (t->edit == NULL)
return S_OK;
// set t->edit to NULL now so we don't trigger commits on focus killed
edit = t->edit;
t->edit = NULL;
if (DestroyWindow(edit) == 0) {
logLastError(L"DestroyWindow()");
return E_FAIL;
}
return S_OK;
}
HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult)
{
LVHITTESTINFO ht;
uiprivTableColumnParams *p;
int modelColumn, editableColumn;
bool text, checkbox;
uiTableValue *value;
int checked, editable;
HRESULT hr;
ZeroMemory(&ht, sizeof (LVHITTESTINFO));
ht.pt = nm->ptAction;
if (SendMessageW(t->hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM) (&ht)) == (LRESULT) (-1))
goto done;
modelColumn = -1;
editableColumn = -1;
text = false;
checkbox = false;
p = (*(t->columns))[ht.iSubItem];
if (p->textModelColumn != -1) {
modelColumn = p->textModelColumn;
editableColumn = p->textEditableModelColumn;
text = true;
} else if (p->checkboxModelColumn != -1) {
modelColumn = p->checkboxModelColumn;
editableColumn = p->checkboxEditableModelColumn;
checkbox = true;
} else if (p->buttonModelColumn != -1) {
modelColumn = p->buttonModelColumn;
editableColumn = p->buttonClickableModelColumn;
}
if (modelColumn == -1)
goto done;
if (text && t->inDoubleClickTimer)
// don't even ask for info if it's too soon to edit text
goto done;
if (!uiprivTableModelCellEditable(t->model, ht.iItem, editableColumn))
goto done;
if (text) {
hr = openEditControl(t, ht.iItem, ht.iSubItem, p);
if (hr != S_OK)
return hr;
} else if (checkbox) {
if ((ht.flags & LVHT_ONITEMICON) == 0)
goto done;
value = uiprivTableModelCellValue(t->model, ht.iItem, modelColumn);
checked = uiTableValueInt(value);
uiFreeTableValue(value);
value = uiNewTableValueInt(!checked);
uiprivTableModelSetCellValue(t->model, ht.iItem, modelColumn, value);
uiFreeTableValue(value);
} else
uiprivTableModelSetCellValue(t->model, ht.iItem, modelColumn, NULL);
// always refresh the value in case the model rejected it
if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (ht.iItem), 0) == (LRESULT) (-1)) {
logLastError(L"LVM_UPDATE");
return E_FAIL;
}
done:
*lResult = 0;
return S_OK;
}

105
windows/tablemetrics.cpp Normal file
View File

@ -0,0 +1,105 @@
// 14 june 2018
#include "uipriv_windows.hpp"
#include "table.hpp"
static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r)
{
if (hr != S_OK)
return hr;
ZeroMemory(r, sizeof (RECT));
r->left = left;
r->top = top;
if (SendMessageW(t->hwnd, uMsg, wParam, (LPARAM) r) == bad) {
logLastError(L"itemRect() message");
return E_FAIL;
}
return S_OK;
}
HRESULT uiprivTableGetMetrics(uiTable *t, int iItem, int iSubItem, uiprivTableMetrics **mout)
{
uiprivTableMetrics *m;
uiprivTableColumnParams *p;
LRESULT state;
HWND header;
RECT r;
HRESULT hr;
if (mout == NULL)
return E_POINTER;
m = uiprivNew(uiprivTableMetrics);
p = (*(t->columns))[iSubItem];
m->hasText = p->textModelColumn != -1;
m->hasImage = (p->imageModelColumn != -1) || (p->checkboxModelColumn != -1);
state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, iItem, LVIS_FOCUSED | LVIS_SELECTED);
m->focused = (state & LVIS_FOCUSED) != 0;
m->selected = (state & LVIS_SELECTED) != 0;
// TODO check LRESULT bad parameters here
hr = itemRect(S_OK, t, LVM_GETITEMRECT, iItem,
LVIR_BOUNDS, 0, FALSE, &(m->itemBounds));
hr = itemRect(hr, t, LVM_GETITEMRECT, iItem,
LVIR_ICON, 0, FALSE, &(m->itemIcon));
hr = itemRect(hr, t, LVM_GETITEMRECT, iItem,
LVIR_LABEL, 0, FALSE, &(m->itemLabel));
hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem,
LVIR_BOUNDS, iSubItem, 0, &(m->subitemBounds));
hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem,
LVIR_ICON, iSubItem, 0, &(m->subitemIcon));
if (hr != S_OK)
goto fail;
// LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as
// LVIR_BOUNDS, so we can't use that directly. Instead, let's
// assume the text is immediately after the icon. The correct
// rect will be determined by
// computeOtherRectsAndDrawBackgrounds() above.
m->subitemLabel = m->subitemBounds;
m->subitemLabel.left = m->subitemIcon.right;
// And on iSubItem == 0, LVIF_GETSUBITEMRECT still includes
// all the subitems, which we don't want.
if (iSubItem == 0) {
m->subitemBounds.right = m->itemLabel.right;
m->subitemLabel.right = m->itemLabel.right;
}
header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0);
m->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0);
if (ImageList_GetIconSize(t->imagelist, &(m->cxIcon), &(m->cyIcon)) == 0) {
logLastError(L"ImageList_GetIconSize()");
hr = E_FAIL;
goto fail;
}
r = m->subitemLabel;
if (!m->hasText && !m->hasImage)
r = m->subitemBounds;
else if (!m->hasImage && iSubItem != 0)
// By default, this will include images; we're not drawing
// images, so we will manually draw over the image area.
// There's a second part to this; see below.
r.left = m->subitemBounds.left;
m->realTextBackground = r;
m->realTextRect = r;
// TODO confirm whether this really happens on column 0 as well
if (m->hasImage && iSubItem != 0)
// Normally there's this many hard-coded logical units
// of blank space, followed by the background, followed
// by a bitmap margin's worth of space. This looks bad,
// so we overrule that to start the background immediately
// and the text after the hard-coded amount.
m->realTextRect.left += 2;
else if (iSubItem != 0)
// In the case of subitem text without an image, we draw
// text one bitmap margin away from the left edge.
m->realTextRect.left += m->bitmapMargin;
*mout = m;
return S_OK;
fail:
uiprivFree(m);
*mout = NULL;
return hr;
}

View File

@ -163,3 +163,10 @@ extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt);
// draw.cpp
extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r);
// image.cpp
extern IWICImagingFactory *uiprivWICFactory;
extern HRESULT uiprivInitImage(void);
extern void uiprivUninitImage(void);
extern IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc);
extern HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb);

View File

@ -37,11 +37,14 @@
#ifndef RC_INVOKED
#include <commctrl.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <windowsx.h>
#include <shobjidl.h>
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <wincodec.h>
#include <stdint.h>
#include <string.h>
@ -57,4 +60,5 @@
#include <unordered_map>
#include <sstream>
#include <functional>
#include <utility>
#endif