font browser

* update font browser class to use std::list vice
    computed array size to enumerate system fonts
This commit is contained in:
dave-w1hkj 2024-01-07 10:20:11 -06:00
parent afb90bd6d5
commit 73db3b6b0a
2 changed files with 237 additions and 128 deletions

View file

@ -27,12 +27,16 @@
#ifndef FONTBROWSER_H
#define FONTBROWSER_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
@ -50,13 +54,35 @@ public:
};
// 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:
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;
@ -69,13 +95,15 @@ private:
Fl_Return_Button *btn_OK;
Fl_Button *btn_Cancel;
Fl_Button *btn_Color;
Fl_Check_Button *btn_fixed;
Preview_Box *box_Example;
void FontSort();
Fl_Callback* callback_;
public:
Font_Browser(int x = 100, int y = 100, int w = 430, int h = 225, const char *lbl = "Font Browser");
~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();
@ -92,7 +120,8 @@ public:
const char *fontName() { return lst_Font->text(lst_Font->value()); }
void fontName(const char* n);
static bool fixed_width(Fl_Font f);
static bool fixed_width(Fl_Font f);
void fontFilter(filter_t filter);
};

View file

@ -29,7 +29,13 @@
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cassert>
#include <iostream>
#include <list>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <FL/Fl.H>
#include <FL/Fl_Color_Chooser.H>
@ -37,12 +43,34 @@
#include "font_browser.h"
#include "flslider2.h"
#include "util.h"
#include "gettext.h"
#include "threads.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()
@ -53,22 +81,35 @@ void Font_Browser::ColorSelect()
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);
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();
fb->hide();
else if (w == fb->btn_OK) {
if (fb->callback_)
(*fb->callback_)(fb, fb->data_);
}
else if (w == fb->btn_Color)
fb->ColorSelect();
fb->ColorSelect();
else if (w == fb->lst_Font)
fb->FontNameSelect();
fb->FontNameSelect();
else {
if (w == fb->lst_Size)
fb->txt_Size->value(strtol(fb->lst_Size->text(fb->lst_Size->value()), NULL, 10));
@ -77,16 +118,6 @@ void Font_Browser::fb_callback(Fl_Widget* w, void* arg)
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);
}
// Font Name changed callback
void Font_Browser::FontNameSelect()
{
@ -99,29 +130,25 @@ void Font_Browser::FontNameSelect()
// get sizes and fill browser; skip first element if it is zero
lst_Size->clear();
int nsizes, *sizes;
size_t szt;
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)) {
szt = sizes[i];
lst_Size->add(buf, (void*)szt);//sizes[i]);
}
if ((size_t)snprintf(buf, sizeof(buf), "%d", sizes[i]) < sizeof(buf))
lst_Size->add(buf, reinterpret_cast<void*>(sizes[i]));
// scalable font with no suggested sizes
if (!lst_Size->size()) {
for (int i = 1; i <= 48; i++) {
snprintf(buf, sizeof(buf), "%d", i);
szt = i;
lst_Size->add(buf, (void*)szt);
}
for (int i = 1; i <= 48; i++) {
snprintf(buf, sizeof(buf), "%d", i);
lst_Size->add(buf, reinterpret_cast<void*>(i));
}
}
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);
@ -138,6 +165,10 @@ Font_Browser::Font_Browser(int x, int y, int w, int h, const char *lbl )
lst_Size->type(FL_HOLD_BROWSER);
lst_Size->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_OK = new Fl_Return_Button(345, 40, 80, 25, _("&OK"));
btn_OK->shortcut(0x8006f);
btn_OK->callback(fb_callback, this);
@ -149,11 +180,15 @@ Font_Browser::Font_Browser(int x, int y, int w, int h, const char *lbl )
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);
box_Example = new Preview_Box(5, 145, 420, 75, _("That crazy fox jumped over the dog again!\n"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789\n"
"!\"#$%&'()*+,-./:;<=>?@@[\\]^_`{|}~"));
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);
@ -161,37 +196,90 @@ Font_Browser::Font_Browser(int x, int y, int w, int h, const char *lbl )
set_modal();
end();
// Initializations
// Initializations
this->callback_ = 0; // Initialize Widgets callback
this->data_ = 0; // And the data
numfonts = Fl::set_fonts(0); // Nr of fonts available on the server
std::string fntname;
bool ok = true;
const char* name;
size_t szt;
for(int i = 0; i < numfonts; i++) {
name = Fl::get_font_name((Fl_Font)i);
if (isalpha(*name)) {
szt = i;
lst_Font->add(name, (void *)szt);
if (instance == 0) {
++instance;
numfonts = Fl::set_fonts("*"); // Nr of fonts available on the server
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();
}
}
FontSort();
fontnbr = FL_HELVETICA;;
fontsize = FL_NORMAL_SIZE; // Font Size to be used
fontsize = FL_NORMAL_SIZE;
fontcolor = FL_FOREGROUND_COLOR;
filter = ALL_TYPES;
lst_Font->value(1);
FontNameSelect();
//! Fl::focus(lst_Font);
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;
@ -211,10 +299,10 @@ void Font_Browser::fontSize(int s)
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;
}
if ((intptr_t)lst_Size->data(i) == fontsize) {
lst_Size->value(i);
break;
}
}
txt_Size->value(s);
}
@ -237,97 +325,89 @@ void Font_Browser::fontName(const char* n)
}
}
bool Font_Browser::fixed_width(Fl_Font f)
{
fl_font(f, FL_NORMAL_SIZE);
return fl_width('X') == fl_width('i');
}
#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 (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;
}
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->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(".") == fl_width("W");
}
//----------------------------------------------------------------------
Preview_Box::Preview_Box(int x, int y, int w, int h, const char* l)
: Fl_Widget(x, y, w, h, l)
: Fl_Widget(x, y, w, h, l)
{
fontName = 1;
fontSize = FL_NORMAL_SIZE;