font browser

* change font browser class to use std::list to enumarate fonts
This commit is contained in:
dave-w1hkj 2024-01-07 10:31:30 -06:00
parent daec9188e4
commit 7563cf59d5
2 changed files with 379 additions and 260 deletions

View file

@ -37,65 +37,102 @@
#include <FL/Fl_Return_Button.H>
#include "flslider2.h"
#include <string>
#include <list>
#include <FL/Enumerations.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Browser.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Check_Button.H>
#include "flslider2.h"
// Preview box for showing font
class Preview_Box : public Fl_Widget
{
private:
int fontName;
int fontSize;
Fl_Color fontColor;
int fontName;
int fontSize;
Fl_Color fontColor;
void draw();
void draw();
public:
Preview_Box(int x, int y, int w, int h, const char* l);
void SetFont( int fontname, int fontsize, Fl_Color c);
Preview_Box(int x, int y, int w, int h, const char* l);
void SetFont( int fontname, int fontsize, Fl_Color c);
};
// Font browser widget
struct font_pair {
int nbr;
std::string name;
font_pair() {
nbr = 0;
name.clear();
}
~font_pair() {
}
};
class Font_Browser : public Fl_Window
{
public:
enum filter_t { FIXED_WIDTH, VARIABLE_WIDTH, ALL_TYPES };
friend void *find_fixed_fonts(void *);
enum filter_t { FIXED_WIDTH, VARIABLE_WIDTH, ALL_TYPES };
// these are shared by all instances of Font_Browser
// created for instance 1 and deleted for instance 0
static int *fixed;
static std::list<font_pair> font_list;
font_pair nufont;
static int instance;
static int numfonts;
private:
int numfonts;
Fl_Font fontnbr;
int fontsize;
Fl_Color fontcolor;
filter_t filter;
void *data_;
Fl_Browser *lst_Font;
Fl_Browser *lst_Size;
Fl_Value_Input2 *txt_Size;
Fl_Return_Button *btn_OK;
Fl_Button *btn_Cancel;
Fl_Button *btn_Color;
Preview_Box *box_Example;
Fl_Font fontnbr;
int fontsize;
Fl_Color fontcolor;
filter_t filter;
void *data_;
void FontSort();
Fl_Callback* callback_;
Fl_Browser *lst_Font;
Fl_Browser *lst_Size;
Fl_Value_Input2 *txt_Size;
Fl_Return_Button *btn_OK;
Fl_Button *btn_Cancel;
Fl_Button *btn_Color;
Fl_Check_Button *btn_fixed;
Preview_Box *box_Example;
Fl_Callback* callback_;
public:
Font_Browser(int x = 100, int y = 100, int w = 430, int h = 225, const char *lbl = "Font Browser");
void callback(Fl_Callback* cb, void *d = 0) { callback_ = cb; data_ = d; }
static void fb_callback(Fl_Widget* w, void* arg);
void FontNameSelect();
void ColorSelect();
Font_Browser(int x = 100, int y = 100, int w = 430, int h = 225, const char *lbl = "Font Browser");
~Font_Browser();
int numFonts() { return numfonts; }
void fontNumber(Fl_Font n);
Fl_Font fontNumber() { return fontnbr; }
void fontSize(int s);
int fontSize() { return fontsize; }
void fontColor(Fl_Color c);
Fl_Color fontColor() { return fontcolor; };
void callback(Fl_Callback* cb, void *d = 0) { callback_ = cb; data_ = d; }
static void fb_callback(Fl_Widget* w, void* arg);
void FontNameSelect();
void ColorSelect();
const char *fontName() { return lst_Font->text(lst_Font->value()); }
void fontName(const char* n);
int numFonts() { return numfonts; }
void fontNumber(Fl_Font n);
Fl_Font fontNumber() { return fontnbr; }
void fontSize(int s);
int fontSize() { return fontsize; }
void fontColor(Fl_Color c);
Fl_Color fontColor() { return fontcolor; };
static bool fixed_width(Fl_Font f);
void fontFilter(filter_t filter);
const char *fontName() { return lst_Font->text(lst_Font->value()); }
void fontName(const char* n);
static bool fixed_width(Fl_Font f);
void fontFilter(filter_t filter);
};
extern Font_Browser* font_browser;

View file

@ -41,307 +41,389 @@
#include "font_browser.h"
#include "flslider2.h"
#include "gettext.h"
#include "util.h"
#include "debug.h"
void find_fonts();
Font_Browser* font_browser;
//======================================================================
// static members
int Font_Browser::instance = 0;
int *Font_Browser::fixed = 0;
int Font_Browser::numfonts = 100;
std::list<font_pair> Font_Browser::font_list;
//======================================================================
static inline std::string uucase(std::string s) {
for (size_t n = 0; n < s.length(); n++) s[n] = toupper(s[n]);
return s;
}
static bool font_compare(font_pair &p1, font_pair &p2)
{
if (uucase(p1.name) > uucase(p2.name)) return false;
return true;
}
// Font Color selected
void Font_Browser::ColorSelect()
{
unsigned char r, g, b;
Fl::get_color(fontcolor, r, g, b);
if (fl_color_chooser(_("Font color"), r, g, b) == 0)
return;
fontcolor = fl_rgb_color(r, g, b);
btn_Color->color(fontcolor);
unsigned char r, g, b;
Fl::get_color(fontcolor, r, g, b);
if (fl_color_chooser(_("Font color"), r, g, b) == 0)
return;
fontcolor = fl_rgb_color(r, g, b);
btn_Color->color(fontcolor);
btn_Color->labelcolor( fl_contrast(FL_BLACK, fontcolor));
}
void Font_Browser::fb_callback(Fl_Widget* w, void* arg)
{
Font_Browser* fb = reinterpret_cast<Font_Browser*>(arg);
Font_Browser* fb = reinterpret_cast<Font_Browser*>(arg);
if (w == fb->btn_Cancel)
if (w == fb->btn_fixed) {
if (fb->btn_fixed->value())
fb->fontFilter(FIXED_WIDTH);
else
fb->fontFilter(ALL_TYPES);
return;
}
if (w == fb->btn_Cancel)
fb->hide();
else if (w == fb->btn_OK) {
if (fb->callback_)
(*fb->callback_)(fb, fb->data_);
}
else if (w == fb->btn_Color)
fb->ColorSelect();
else if (w == fb->lst_Font)
fb->FontNameSelect();
else {
if (w == fb->lst_Size)
fb->txt_Size->value(strtol(fb->lst_Size->text(fb->lst_Size->value()), NULL, 10));
fb->fontsize = static_cast<int>(fb->txt_Size->value());
}
fb->box_Example->SetFont(fb->fontnbr, fb->fontsize, fb->fontcolor);
}
// Sort the font list
void Font_Browser::FontSort()
{
int size = lst_Font->size();
for ( int t = 1; t <= size - 1; t++ )
for ( int r = t+1; r <= size; r++ )
if ( strcasecmp(lst_Font->text(t), lst_Font->text(r)) > 0 )
lst_Font->swap(t,r);
else if (w == fb->btn_OK) {
if (fb->callback_)
(*fb->callback_)(fb, fb->data_);
}
else if (w == fb->btn_Color)
fb->ColorSelect();
else if (w == fb->lst_Font)
fb->FontNameSelect();
else {
if (w == fb->lst_Size)
fb->txt_Size->value(strtol(fb->lst_Size->text(fb->lst_Size->value()), NULL, 10));
fb->fontsize = static_cast<int>(fb->txt_Size->value());
}
fb->box_Example->SetFont(fb->fontnbr, fb->fontsize, fb->fontcolor);
}
// Font Name changed callback
void Font_Browser::FontNameSelect()
{
int fn = lst_Font->value();
if (!fn)
return;
int fn = lst_Font->value();
if (!fn)
return;
fontnbr = (Fl_Font)reinterpret_cast<intptr_t>(lst_Font->data(fn));
fontnbr = (Fl_Font)reinterpret_cast<intptr_t>(lst_Font->data(fn));
// get sizes and fill browser; skip first element if it is zero
lst_Size->clear();
int nsizes, *sizes;
char buf[4];
nsizes = Fl::get_font_sizes(fontnbr, sizes);
//
for (int i = !*sizes; i < nsizes; i++)
// get sizes and fill browser; skip first element if it is zero
lst_Size->clear();
int nsizes, *sizes;
char buf[4];
nsizes = Fl::get_font_sizes(fontnbr, sizes);
//
for (int i = !*sizes; i < nsizes; i++)
if ((size_t)snprintf(buf, sizeof(buf), "%d", sizes[i]) < sizeof(buf))
lst_Size->add(buf, reinterpret_cast<void *>(sizes[i]));
lst_Size->add(buf, reinterpret_cast<void*>(sizes[i]));
// scalable font with no suggested sizes
if (!lst_Size->size()) {
// scalable font with no suggested sizes
if (!lst_Size->size()) {
for (int i = 1; i <= 48; i++) {
snprintf(buf, sizeof(buf), "%d", i);
lst_Size->add(buf, reinterpret_cast<void *>(i));
snprintf(buf, sizeof(buf), "%d", i);
lst_Size->add(buf, reinterpret_cast<void*>(i));
}
}
fontSize(fontsize);
}
fontSize(fontsize);
}
Font_Browser::Font_Browser(int x, int y, int w, int h, const char *lbl )
: Fl_Window(x, y, w, h, lbl)
: Fl_Window(x, y, w, h, lbl)
{
lst_Font = new Fl_Browser(5, 15, 280, 125, _("Font:"));
lst_Font->align(FL_ALIGN_TOP_LEFT);
lst_Font->type(FL_HOLD_BROWSER);
lst_Font->callback(fb_callback, this);
lst_Font = new Fl_Browser(5, 15, 280, 125, _("Font:"));
lst_Font->align(FL_ALIGN_TOP_LEFT);
lst_Font->type(FL_HOLD_BROWSER);
lst_Font->callback(fb_callback, this);
txt_Size = new Fl_Value_Input2(290, 15, 50, 22, _("Size:"));
txt_Size->align(FL_ALIGN_TOP_LEFT);
txt_Size->range(1.0, 48.0);
txt_Size->step(1.0);
txt_Size->callback(fb_callback, this);
txt_Size = new Fl_Value_Input2(290, 15, 50, 22, _("Size:"));
txt_Size->align(FL_ALIGN_TOP_LEFT);
txt_Size->range(1.0, 48.0);
txt_Size->step(1.0);
txt_Size->callback(fb_callback, this);
lst_Size = new Fl_Browser(290, 40, 50, 100);
lst_Size->type(FL_HOLD_BROWSER);
lst_Size->callback(fb_callback, this);
lst_Size = new Fl_Browser(290, 40, 50, 100);
lst_Size->type(FL_HOLD_BROWSER);
lst_Size->callback(fb_callback, this);
btn_OK = new Fl_Return_Button(345, 40, 80, 25, _("&OK"));
btn_OK->shortcut(0x8006f);
btn_OK->callback(fb_callback, this);
btn_fixed = new Fl_Check_Button(345, 15, 18, 18, _("Fixed"));
btn_fixed->callback(fb_callback, this);
btn_fixed->value(0);
btn_Cancel = new Fl_Button(345, 70, 80, 25, _("Cancel"));
btn_Cancel->labelsize(12);
btn_Cancel->callback(fb_callback, this);
btn_OK = new Fl_Return_Button(345, 40, 80, 25, _("&OK"));
btn_OK->shortcut(0x8006f);
btn_OK->callback(fb_callback, this);
btn_Color = new Fl_Button(345, 100, 80, 25, _("Color"));
btn_Color->down_box(FL_BORDER_BOX);
btn_Color->color(FL_FOREGROUND_COLOR);
btn_Color->callback(fb_callback, this);
btn_Cancel = new Fl_Button(345, 70, 80, 25, _("Cancel"));
btn_Cancel->labelsize(12);
btn_Cancel->callback(fb_callback, this);
box_Example = new Preview_Box(5, 145, 420, 75, _("That crazy fox jumped over the dog again!\n"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789\n"
"!\"#$%&'()*+,-./:;<=>?@@[\\]^_`{|}~"));
box_Example->box(FL_DOWN_BOX);
box_Example->align(FL_ALIGN_WRAP|FL_ALIGN_CLIP|FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
resizable(box_Example);
btn_Color = new Fl_Button(345, 100, 80, 25, _("Color"));
btn_Color->down_box(FL_BORDER_BOX);
btn_Color->color(FL_FOREGROUND_COLOR);
btn_Color->labelcolor( fl_contrast(FL_BLACK, FL_FOREGROUND_COLOR));
btn_Color->callback(fb_callback, this);
set_modal();
end();
box_Example = new Preview_Box(5, 145, 420, 75,
_("\
abcdefghijklmnopqrstuvwxyz\n\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\n\
0123456789"
) );
box_Example->box(FL_DOWN_BOX);
box_Example->align(FL_ALIGN_WRAP|FL_ALIGN_CLIP|FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
resizable(box_Example);
// Initializations
set_modal();
end();
this->callback_ = 0; // Initialize Widgets callback
this->data_ = 0; // And the data
// Initializations
numfonts = Fl::set_fonts(0); // Nr of fonts available on the server
this->callback_ = 0; // Initialize Widgets callback
this->data_ = 0; // And the data
const char* name;
for(int i = 0; i < numfonts; i++) {
name = Fl::get_font_name((Fl_Font)i);
if (isalpha(*name))
lst_Font->add(name, reinterpret_cast<void *>(i));
}
FontSort();
std::string fntname;
bool ok = true;
fontnbr = FL_HELVETICA;;
fontsize = FL_NORMAL_SIZE; // Font Size to be used
fontcolor = FL_FOREGROUND_COLOR;
filter = ALL_TYPES;
if (instance == 0) {
lst_Font->value(1);
FontNameSelect();
++instance;
//! Fl::focus(lst_Font);
numfonts = Fl::set_fonts("*"); // Nr of fonts available on the server
xclass(PACKAGE_NAME);
font_list.clear();
fixed = new int[numfonts];
int j = 0;
for (int i = 0; i < numfonts; i++) {
fntname = Fl::get_font_name((Fl_Font)i);
ok = true;
for (size_t k = 0; k < fntname.length(); k++) {
if (fntname[k] < ' ' || fntname[k] > 'z' ||
fntname[k] == '\\' || fntname[k] == '@') { // disallowed chars in browser widget
ok = false;
break;
}
}
if (fntname.empty()) ok = false;
if (ok) {
nufont.name = fntname;
nufont.nbr = i;
font_list.push_back(nufont);
j++;
}
}
numfonts = j;
font_list.sort(font_compare);
std::list<font_pair> temp_list = font_list;
int i = 0;
while (!temp_list.empty()) {
nufont = temp_list.front();
lst_Font->add( nufont.name.c_str(), reinterpret_cast<void *>(nufont.nbr) );
temp_list.pop_front();
fixed[i++] = 0;
}
find_fonts();
} else {
++instance;
std::list<font_pair> temp_list = font_list;
while (!temp_list.empty()) {
nufont = temp_list.front();
lst_Font->add( nufont.name.c_str(), reinterpret_cast<void *>(nufont.nbr) );
temp_list.pop_front();
}
}
fontnbr = FL_HELVETICA;;
fontsize = FL_NORMAL_SIZE;
fontcolor = FL_FOREGROUND_COLOR;
filter = ALL_TYPES;
lst_Font->value(1);
FontNameSelect();
xclass(PACKAGE_NAME);
}
Font_Browser::~Font_Browser()
{
--instance;
if (instance == 0) {
delete [] fixed;
fixed = 0;
font_list.clear();
}
}
void Font_Browser::fontNumber(Fl_Font n)
{
fontnbr = n;
lst_Font->value(1);
int s = lst_Font->size();
for (int i = 1; i < s; i++ ) {
if ((Fl_Font)reinterpret_cast<intptr_t>(lst_Font->data(i)) == n) {
lst_Font->value(i);
FontNameSelect();
break;
}
}
fontnbr = n;
lst_Font->value(1);
int s = lst_Font->size();
for (int i = 1; i < s; i++ ) {
if ((Fl_Font)reinterpret_cast<intptr_t>(lst_Font->data(i)) == n) {
lst_Font->value(i);
FontNameSelect();
break;
}
}
}
void Font_Browser::fontSize(int s)
{
fontsize = s;
int n = lst_Size->size();
for (int i = 1; i < n; i++) {
fontsize = s;
int n = lst_Size->size();
for (int i = 1; i < n; i++) {
if ((intptr_t)lst_Size->data(i) == fontsize) {
lst_Size->value(i);
break;
lst_Size->value(i);
break;
}
}
txt_Size->value(s);
}
txt_Size->value(s);
}
void Font_Browser::fontColor(Fl_Color c)
{
btn_Color->color(fontcolor = c);
box_Example->SetFont(fontnbr, fontsize, fontcolor);
box_Example->redraw();
btn_Color->color(fontcolor = c);
box_Example->SetFont(fontnbr, fontsize, fontcolor);
box_Example->redraw();
}
void Font_Browser::fontName(const char* n)
{
int s = lst_Font->size();
for (int i = 1; i < s; i++) {
if (!strcmp(lst_Font->text(i), n)) {
lst_Font->value(i);
FontNameSelect();
}
}
int s = lst_Font->size();
for (int i = 1; i < s; i++) {
if (!strcmp(lst_Font->text(i), n)) {
lst_Font->value(i);
FontNameSelect();
}
}
}
void Font_Browser::fontFilter(filter_t filter)
{
int s = lst_Font->size();
switch (filter) {
case FIXED_WIDTH:
for (int i = 0; i < s; i++) {
if (fixed[i])
lst_Font->show(i + 1);
else
lst_Font->hide(i + 1);
}
btn_fixed->value(1);
break;
case VARIABLE_WIDTH:
for (int i = 0; i < s; i++) {
if (!fixed[i])
lst_Font->show(i + 1);
else
lst_Font->hide(i + 1);
}
btn_fixed->value(0);
break;
case ALL_TYPES:
for (int i = 0; i < s; i++)
lst_Font->show(i + 1);
btn_fixed->value(0);
break;
}
lst_Font->redraw();
lst_Font->topline(lst_Font->value());
}
// separate thread used to evaluate fixed / proportional fonts
// friend of Font_Browser
// launched by Font_Browser instance 1
static pthread_t find_font_thread;
static int is_fixed;
Fl_Font test_font;
void font_test(void *)
{
fl_font(test_font, FL_NORMAL_SIZE);
is_fixed = (fl_width(".") == fl_width("W"));
}
void *find_fixed_fonts(void *)
{
std::list<font_pair> temp_list = Font_Browser::font_list;
int i = 0;
while (!temp_list.empty()) {
test_font = temp_list.front().nbr;
is_fixed = -1;
Fl::awake(font_test);
while (is_fixed == -1) {
MilliSleep(1);
}
Font_Browser::fixed[i] = is_fixed;
i++;
temp_list.pop_front();
}
return NULL;
}
void find_fonts()
{
if (pthread_create(&find_font_thread, NULL, find_fixed_fonts, NULL) < 0) {
LOG_ERROR("%s", "pthread_create find_fixed_fonts failed");
}
return;
}
bool Font_Browser::fixed_width(Fl_Font f)
{
fl_font(f, FL_NORMAL_SIZE);
return fl_width('X') == fl_width('i');
return fl_width(".") == fl_width("W");
}
#include <vector>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Progress.H>
class Progress_Window : public Fl_Double_Window
{
public:
Progress_Window(float min = 0.0f, float max = 100.0f, const char* l = 0)
: Fl_Double_Window(200, 34), ps(5, 5, 190, 24, l)
{
end();
range(min, max);
ps.align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE);
ps.selection_color(FL_SELECTION_COLOR);
set_modal();
callback(nop);
if (l && *l) {
fl_font(FL_HELVETICA, FL_NORMAL_SIZE);
int s = (int)(fl_width(l) + fl_width('W'));
if (s > ps.w()) {
ps.size(s, ps.h());
size(ps.w() + 10, h());
}
}
position(Fl::event_x_root() - w() / 2, Fl::event_y_root() - h());
xclass(PACKAGE_TARNAME);
show();
}
void range(float min, float max) { ps.minimum(min); ps.maximum(max); }
void value(float val) { ps.value(val); }
static void nop(Fl_Widget*, void*) { }
private:
Fl_Progress ps;
};
void Font_Browser::fontFilter(filter_t filter)
{
if (this->filter == filter)
return;
int s = lst_Font->size();
static std::vector<bool> fixed;
if (fixed.empty()) {
Progress_Window pw(1, s, _("Reading fonts..."));
fixed.resize(s);
for (int i = 1; i < s; i++) {
fixed[i] = fixed_width((Fl_Font)(intptr_t)(lst_Font->data(i)));
pw.value(i);
Fl::check();
}
}
switch (this->filter = filter) {
case FIXED_WIDTH:
for (int i = 1; i < s; i++) {
if (fixed[i])
lst_Font->show(i);
else
lst_Font->hide(i);
}
break;
case VARIABLE_WIDTH:
for (int i = 1; i < s; i++) {
if (!fixed[i])
lst_Font->show(i);
else
lst_Font->hide(i);
}
break;
case ALL_TYPES:
for (int i = 1; i < s; i++)
lst_Font->show(i);
break;
}
lst_Font->topline(lst_Font->value());
}
//////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------
Preview_Box::Preview_Box(int x, int y, int w, int h, const char* l)
: Fl_Widget(x, y, w, h, l)
{
fontName = 1;
fontSize = FL_NORMAL_SIZE;
box(FL_DOWN_BOX);
color(FL_BACKGROUND2_COLOR);
fontColor = FL_FOREGROUND_COLOR;
fontName = 1;
fontSize = FL_NORMAL_SIZE;
box(FL_DOWN_BOX);
color(FL_BACKGROUND2_COLOR);
fontColor = FL_FOREGROUND_COLOR;
}
void Preview_Box::draw()
{
draw_box();
fl_font((Fl_Font)fontName, fontSize);
fl_color(fontColor);
fl_draw(label(), x()+3, y()+3, w()-6, h()-6, align());
draw_box();
fl_font((Fl_Font)fontName, fontSize);
fl_color(fontColor);
fl_draw(label(), x()+3, y()+3, w()-6, h()-6, align());
}
void Preview_Box::SetFont(int fontname, int fontsize, Fl_Color c)
{
fontName = fontname;
fontSize = fontsize;
fontColor = c;
redraw();
fontName = fontname;
fontSize = fontsize;
fontColor = c;
redraw();
}