|
commit 29531873ea4395a2dc65cefcc28db09569c38c18
Author: Matthias Melcher <git@matthiasm.com>
AuthorDate: Sun Dec 19 01:09:13 2021 +0100
Commit: Matthias Melcher <github@matthiasm.com>
CommitDate: Sun Dec 19 02:03:05 2021 +0100
STR 3289: Fluid i18n, gettext, catguts improvements
Removed some unneeded code.
documentation/src/fluid-gettext.png | Bin 4369 -> 32267 bytes
documentation/src/fluid.dox | 26 ++++-
fluid/Fl_Menu_Type.cxx | 186 +++++++++++++++++++++++++-----------
fluid/Fl_Type.cxx | 1 +
fluid/Fl_Window_Type.cxx | 31 ++++--
fluid/Fluid_Image.cxx | 5 +
fluid/Fluid_Image.h | 1 +
fluid/alignment_panel.cxx | 42 ++++++--
fluid/alignment_panel.fl | 36 ++++---
fluid/alignment_panel.h | 2 +
fluid/code.cxx | 45 +++++++--
fluid/file.cxx | 19 ++--
fluid/fluid.cxx | 41 +++++++-
fluid/fluid.h | 2 +
test/preferences.fl | 11 ++-
15 files changed, 334 insertions(+), 114 deletions(-)
diff --git documentation/src/fluid-gettext.png documentation/src/fluid-gettext.png
index 8303062..f961c69 100644
Binary files documentation/src/fluid-gettext.png and documentation/src/fluid-gettext.png differ
diff --git documentation/src/fluid.dox documentation/src/fluid.dox
index fd4cf4a..b0682be 100644
--- documentation/src/fluid.dox
+++ documentation/src/fluid.dox
@@ -1503,8 +1503,10 @@ existing template, and click "Delete Template".
\section fluid_i18n Internationalization with FLUID
FLUID supports internationalization (I18N for short) of label
-strings used by widgets. The preferences window
-(<tt>Ctrl+p</tt>) provides access to the I18N options.
+strings and tooltips used by widgets. The GNU gettext option also
+supports deferred translation of statically initialised menu item
+labels. The preferences window (<tt>Ctrl+p</tt>) provides access
+to the I18N options.
\subsection fluid_i18n_methods I18N Methods
@@ -1530,10 +1532,10 @@ need to call \p setlocale() and \p textdomain() or
message file.
To use GNU gettext for I18N, open the preferences window and
-choose "GNU gettext" from the \b Use: chooser. Two new input
+choose "GNU gettext" from the \b Use: chooser. Four new input
fields will then appear to control the include file and
-function/macro name to use when retrieving the localized label
-strings.
+function/macro names to use when retrieving localized label
+strings in dynamic allocation and static initialisation.
\image html fluid-gettext.png "Internationalization using GNU gettext"
\image latex fluid-gettext.png "Internationalization using GNU gettext" width=10cm
@@ -1543,10 +1545,24 @@ field controls the header file to include for
I18N; by default this is \b <libintl.h>, the
standard I18N file for GNU gettext.
+If the \b Conditional: field contains a macro name, i18n will only be compiled
+into the product if this macro is defined. The build system should define the
+macro only if all required headers and libraries are available. If the macro
+is not defined, no headers are included and `gettext` passes text through
+untranslated.
+
The \b Function: field controls the function (or macro) that
will retrieve the localized message; by default the
\p gettext function will be called.
+The **Static Function:** field names a macro that will mark static text
+fields for extraction with the `xgettext` tool. The default macro name is
+\p gettext_noop and will be defined as `#define gettext_noop(text) text`
+right after the `#include` statement. Fluid will do its best to call
+`gettext` on static texts after the textdomain was set by the user.
+
+\see [GNU gettext special cases](https://www.gnu.org/software/gettext/manual/html_node/Special-cases.html)
+
\subsection fluid_catgets_i18n Using POSIX catgets for I18N
FLUID's code support for POSIX catgets allows you to use a
diff --git fluid/Fl_Menu_Type.cxx fluid/Fl_Menu_Type.cxx
index d7e5fc5..e520087 100644
--- fluid/Fl_Menu_Type.cxx
+++ fluid/Fl_Menu_Type.cxx
@@ -39,6 +39,7 @@
#include <FL/Fl_Menu_Button.H>
#include <FL/Fl_Output.H>
#include <FL/fl_draw.H>
+#include <FL/Fl_Multi_Label.H>
#include "../src/flstring.h"
#include <stdio.h>
@@ -53,6 +54,32 @@ Fl_Menu_Item menu_item_type_menu[] = {
static char submenuflag;
static uchar menuitemtype = 0;
+static void delete_dependents(Fl_Menu_Item *m) {
+ if (!m)
+ return;
+ int level = 0;
+ for (;;m++) {
+ if (m->label()==NULL) {
+ if (level==0) {
+ break;
+ } else {
+ level--;
+ }
+ }
+ if (m->flags&FL_SUBMENU)
+ level++;
+ if (m->labeltype()==FL_MULTI_LABEL)
+ delete (Fl_Multi_Label*)m->label();
+ }
+}
+
+static void delete_menu(Fl_Menu_Item *m) {
+ if (!m)
+ return;
+ delete_dependents(m);
+ delete[] m;
+}
+
void Fl_Input_Choice_Type::build_menu() {
Fl_Input_Choice* w = (Fl_Input_Choice*)o;
// count how many Fl_Menu_Item structures needed:
@@ -63,23 +90,35 @@ void Fl_Input_Choice_Type::build_menu() {
n++;
}
if (!n) {
- if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+ if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
w->menu(0);
menusize = 0;
} else {
n++; // space for null at end of menu
if (menusize<n) {
- if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+ if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
menusize = n+10;
w->menu(new Fl_Menu_Item[menusize]);
+ } else {
+ if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu()));
}
// fill them all in:
Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu());
int lvl = level+1;
for (q = next; q && q->level > level; q = q->next) {
Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q;
- if (i->o->image()) i->o->image()->label(m);
- else {
+ if (i->o->image()) {
+ if (i->o->label() && i->o->label()[0]) {
+ Fl_Multi_Label *ml = new Fl_Multi_Label;
+ ml->labela = (char*)i->o->image();
+ ml->labelb = i->o->label();
+ ml->typea = FL_IMAGE_LABEL;
+ ml->typeb = FL_NORMAL_LABEL;
+ ml->label(m);
+ } else {
+ i->o->image()->label(m);
+ }
+ } else {
m->label(i->o->label() ? i->o->label() : "(nolabel)");
m->labeltype(i->o->labeltype());
}
@@ -198,6 +237,8 @@ const char* Fl_Menu_Item_Type::menu_name(int& i) {
}
void Fl_Menu_Item_Type::write_static() {
+ if (image && label() && label()[0])
+ write_declare("#include <FL/Fl_Multi_Label.H>");
if (callback() && is_name(callback()) && !user_defined(callback()))
write_declare("extern void %s(Fl_Menu_*, %s);", callback(),
user_data_type() ? user_data_type() : "void*");
@@ -265,11 +306,9 @@ void Fl_Menu_Item_Type::write_static() {
const char* k = class_name(1);
if (k) {
int i;
- if (i18n_type) write_c("\nunsigned char %s::%s_i18n_done = 0;", k, menu_name(i));
write_c("\nFl_Menu_Item %s::%s[] = {\n", k, menu_name(i));
} else {
int i;
- if (i18n_type) write_c("\nunsigned char %s_i18n_done = 0;", menu_name(i));
write_c("\nFl_Menu_Item %s[] = {\n", menu_name(i));
}
Fl_Type* t = prev; while (t && t->is_menu_item()) t = t->prev;
@@ -331,9 +370,21 @@ void Fl_Menu_Item_Type::write_item() {
write_comment_inline_c(" ");
write_c(" {");
- if (image) write_c("0");
- else if (label()) write_cstring(label()); // we will call i18n when the widget is instantiated for the first time
- else write_c("\"\"");
+ if (label() && label()[0])
+ switch (i18n_type) {
+ case 1:
+ // we will call i18n when the menu is instantiated for the first time
+ write_c("%s(", i18n_static_function);
+ write_cstring(label());
+ write_c(")");
+ break;
+ case 2:
+ // fall through: strings can't be translated before a catalog is choosen
+ default:
+ write_cstring(label());
+ }
+ else
+ write_c("\"\"");
if (((Fl_Button*)o)->shortcut()) {
int s = ((Fl_Button*)o)->shortcut();
if (use_FL_COMMAND && (s & (FL_CTRL|FL_META))) {
@@ -361,16 +412,22 @@ void Fl_Menu_Item_Type::write_item() {
write_c("},\n");
}
+void start_menu_initialiser(int &initialized, const char *name, int index) {
+ if (!initialized) {
+ initialized = 1;
+ write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), name, index);
+ indentation++;
+ }
+}
+
void Fl_Menu_Item_Type::write_code1() {
int i; const char* mname = menu_name(i);
if (!prev->is_menu_item()) {
// for first menu item, declare the array
if (class_name(1)) {
- if (i18n_type) write_h("%sstatic unsigned char %s_i18n_done;\n", indent(1), mname);
write_h("%sstatic Fl_Menu_Item %s[];\n", indent(1), mname);
} else {
- if (i18n_type) write_h("extern unsigned char %s_i18n_done;\n", mname);
write_h("extern Fl_Menu_Item %s[];\n", mname);
}
}
@@ -401,24 +458,59 @@ void Fl_Menu_Item_Type::write_code1() {
int menuItemInitialized = 0;
// if the name is an array variable, assign the value here
if (name() && strchr(name(), '[')) {
- write_c("%s%s = &%s[%d];\n", indent(), name(), mname, i);
+ write_c("%s%s = &%s[%d];\n", indent_plus(1), name(), mname, i);
}
if (image) {
- menuItemInitialized = 1;
- write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), mname, i);
- image->write_code("o");
+ start_menu_initialiser(menuItemInitialized, mname, i);
+ if (label() && label()[0]) {
+ write_c("%sFl_Multi_Label *ml = new Fl_Multi_Label;\n", indent());
+ write_c("%sml->labela = (char*)", indent());
+ image->write_inline();
+ write_c(";\n");
+ if (i18n_type==0) {
+ write_c("%sml->labelb = o->label();\n", indent());
+ } else if (i18n_type==1) {
+ write_c("%sml->labelb = %s(o->label());\n",
+ indent(), i18n_function);
+ } else if (i18n_type==2) {
+ write_c("%sml->labelb = catgets(%s,%s,i+%d,o->label());\n",
+ indent(), i18n_file[0] ? i18n_file : "_catalog",
+ i18n_set, msgnum());
+ }
+ write_c("%sml->typea = FL_IMAGE_LABEL;\n", indent());
+ write_c("%sml->typeb = FL_NORMAL_LABEL;\n", indent());
+ write_c("%sml->label(o);\n", indent());
+ } else {
+ image->write_code("o");
+ }
+ }
+ if (i18n_type && label() && label()[0]) {
+ Fl_Labeltype t = o->labeltype();
+ if (image) {
+ // label was already copied a few lines up
+ } else if ( t==FL_NORMAL_LABEL || t==FL_SHADOW_LABEL
+ || t==FL_ENGRAVED_LABEL || t==FL_EMBOSSED_LABEL) {
+ start_menu_initialiser(menuItemInitialized, mname, i);
+ if (i18n_type==1) {
+ write_c("%so->label(%s(o->label()));\n",
+ indent(), i18n_function);
+ } else if (i18n_type==2) {
+ write_c("%so->label(catgets(%s,%s,i+%d,o->label()));\n",
+ indent(), i18n_file[0] ? i18n_file : "_catalog",
+ i18n_set, msgnum());
+ }
+ }
}
for (int n=0; n < NUM_EXTRA_CODE; n++) {
if (extra_code(n) && !isdeclare(extra_code(n))) {
- if (!menuItemInitialized) {
- menuItemInitialized = 1;
- write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), mname, i);
- }
- write_c("%s%s\n", indent_plus(1), extra_code(n));
+ start_menu_initialiser(menuItemInitialized, mname, i);
+ write_c("%s%s\n", indent(), extra_code(n));
}
}
- if (menuItemInitialized)
+ if (menuItemInitialized) {
+ indentation--;
write_c("%s}\n",indent());
+ }
}
void Fl_Menu_Item_Type::write_code2() {}
@@ -440,23 +532,35 @@ void Fl_Menu_Type::build_menu() {
n++;
}
if (!n) {
- if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+ if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
w->menu(0);
menusize = 0;
} else {
n++; // space for null at end of menu
if (menusize<n) {
- if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+ if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
menusize = n+10;
w->menu(new Fl_Menu_Item[menusize]);
+ } else {
+ if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu()));
}
// fill them all in:
Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu());
int lvl = level+1;
for (q = next; q && q->level > level; q = q->next) {
Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q;
- if (i->o->image()) i->o->image()->label(m);
- else {
+ if (i->o->image()) {
+ if (i->o->label() && i->o->label()[0]) {
+ Fl_Multi_Label *ml = new Fl_Multi_Label;
+ ml->labela = (char*)i->o->image();
+ ml->labelb = i->o->label();
+ ml->typea = FL_IMAGE_LABEL;
+ ml->typeb = FL_NORMAL_LABEL;
+ ml->label(m);
+ } else {
+ i->o->image()->label(m);
+ }
+ } else {
m->label(i->o->label() ? i->o->label() : "(nolabel)");
m->labeltype(i->o->labeltype());
}
@@ -498,38 +602,6 @@ Fl_Type* Fl_Menu_Type::click_test(int, int) {
void Fl_Menu_Type::write_code2() {
if (next && next->is_menu_item()) {
- if (i18n_type) {
- // take care of i18n now!
- Fl_Menu_Item_Type *mi = (Fl_Menu_Item_Type*)next;
- int i, nItem = 0, nLabel = 0;
- const char *mName = mi->menu_name(i);
- for (Fl_Type* q = next; q && q->is_menu_item(); q = q->next) {
- if (((Fl_Menu_Item_Type*)q)->label()) nLabel++;
- int thislevel = q->level; if (q->is_parent()) thislevel++;
- int nextlevel =
- (q->next && q->next->is_menu_item()) ? q->next->level : next->level+1;
- nItem += 1 + ((thislevel > nextlevel) ? (thislevel-nextlevel) : 0);
- }
- if (nLabel) {
- write_c("%sif (!%s_i18n_done) {\n", indent(), mName);
- write_c("%sint i=0;\n", indent_plus(1));
- write_c("%sfor ( ; i<%d; i++)\n", indent_plus(1), nItem);
- write_c("%sif (%s[i].label())\n", indent_plus(2), mName);
- switch (i18n_type) {
- case 1:
- write_c("%s%s[i].label(%s(%s[i].label()));\n",
- indent_plus(3), mName, i18n_function, mName);
- break;
- case 2:
- write_c("%s%s[i].label(catgets(%s,%s,i+%d,%s[i].label()));\n",
- indent_plus(3), mName, i18n_file[0] ? i18n_file : "_catalog",
- i18n_set, mi->msgnum(), mName);
- break;
- }
- write_c("%s%s_i18n_done = 1;\n", indent_plus(1), mName);
- write_c("%s}\n", indent());
- }
- }
write_c("%s%s->menu(%s);\n", indent(), name() ? name() : "o",
unique_id(this, "menu", name(), label()));
}
diff --git fluid/Fl_Type.cxx fluid/Fl_Type.cxx
index 7b20f53..2669cb7 100644
--- fluid/Fl_Type.cxx
+++ fluid/Fl_Type.cxx
@@ -179,6 +179,7 @@ void delete_all(int selected_only) {
} else f = f->next;
}
if(!selected_only) {
+ // FIXME: undo/redo uses this function, resetting the following preferences randomly
include_H_from_C=1;
use_FL_COMMAND=0;
utf8_in_src = 0;
diff --git fluid/Fl_Window_Type.cxx fluid/Fl_Window_Type.cxx
index 81ac5fb..ddc94fc 100644
--- fluid/Fl_Window_Type.cxx
+++ fluid/Fl_Window_Type.cxx
@@ -45,11 +45,6 @@
int include_H_from_C = 1;
int use_FL_COMMAND = 0;
int utf8_in_src = 0;
-extern int i18n_type;
-extern const char* i18n_include;
-extern const char* i18n_function;
-extern const char* i18n_file;
-extern const char* i18n_set;
extern Fl_Preferences fluid_prefs;
@@ -125,31 +120,43 @@ void i18n_type_cb(Fl_Choice *c, void *) {
switch (i18n_type = c->value()) {
case 0 : /* None */
i18n_include_input->hide();
+ i18n_conditional_input->hide();
i18n_file_input->hide();
i18n_set_input->hide();
i18n_function_input->hide();
+ i18n_static_function_input->hide();
break;
case 1 : /* GNU gettext */
i18n_include_input->value("<libintl.h>");
i18n_include = i18n_include_input->value();
+ i18n_conditional_input->value("");
+ i18n_conditional = i18n_conditional_input->value();
i18n_function_input->value("gettext");
i18n_function = i18n_function_input->value();
+ i18n_static_function_input->value("gettext_noop");
+ i18n_static_function = i18n_static_function_input->value();
i18n_include_input->show();
+ i18n_conditional_input->show();
i18n_file_input->hide();
i18n_set_input->hide();
i18n_function_input->show();
+ i18n_static_function_input->show();
break;
case 2 : /* POSIX cat */
i18n_include_input->value("<nl_types.h>");
+ i18n_include = i18n_include_input->value();
+ i18n_conditional_input->value("");
+ i18n_conditional = i18n_conditional_input->value();
i18n_file_input->value("");
i18n_file = i18n_file_input->value();
i18n_set_input->value("1");
i18n_set = i18n_set_input->value();
i18n_include_input->show();
- i18n_include = i18n_include_input->value();
+ i18n_conditional_input->show();
i18n_file_input->show();
i18n_set_input->show();
i18n_function_input->hide();
+ i18n_static_function_input->hide();
break;
}
@@ -161,10 +168,14 @@ void i18n_text_cb(Fl_Input *i, void *) {
if (i == i18n_function_input)
i18n_function = i->value();
+ else if (i == i18n_static_function_input)
+ i18n_static_function = i->value();
else if (i == i18n_file_input)
i18n_file = i->value();
else if (i == i18n_include_input)
i18n_include = i->value();
+ else if (i == i18n_conditional_input)
+ i18n_conditional = i->value();
set_modflag(1);
}
@@ -187,27 +198,35 @@ void show_project_cb(Fl_Widget *, void *) {
code_file_input->value(code_file_name);
i18n_type_chooser->value(i18n_type);
i18n_function_input->value(i18n_function);
+ i18n_static_function_input->value(i18n_static_function);
i18n_file_input->value(i18n_file);
i18n_set_input->value(i18n_set);
i18n_include_input->value(i18n_include);
+ i18n_conditional_input->value(i18n_conditional);
switch (i18n_type) {
case 0 : /* None */
i18n_include_input->hide();
+ i18n_conditional_input->hide();
i18n_file_input->hide();
i18n_set_input->hide();
i18n_function_input->hide();
+ i18n_static_function_input->hide();
break;
case 1 : /* GNU gettext */
i18n_include_input->show();
+ i18n_conditional_input->show();
i18n_file_input->hide();
i18n_set_input->hide();
i18n_function_input->show();
+ i18n_static_function_input->show();
break;
case 2 : /* POSIX cat */
i18n_include_input->show();
+ i18n_conditional_input->show();
i18n_file_input->show();
i18n_set_input->show();
i18n_function_input->hide();
+ i18n_static_function_input->hide();
break;
}
project_window->hotspot(project_window);
diff --git fluid/Fluid_Image.cxx fluid/Fluid_Image.cxx
index 6576763..a63355a 100644
--- fluid/Fluid_Image.cxx
+++ fluid/Fluid_Image.cxx
@@ -157,6 +157,11 @@ void Fluid_Image::write_code(const char *var, int inactive) {
if (img) write_c("%s%s->%s( %s() );\n", indent(), var, inactive ? "deimage" : "image", function_name_);
}
+void Fluid_Image::write_inline(int inactive) {
+ if (img)
+ write_c("%s()", function_name_);
+}
+
////////////////////////////////////////////////////////////////
diff --git fluid/Fluid_Image.h fluid/Fluid_Image.h
index 99b06d2..d11e248 100644
--- fluid/Fluid_Image.h
+++ fluid/Fluid_Image.h
@@ -41,6 +41,7 @@ public:
void write_static();
void write_initializer(const char *type_name, const char *format, ...);
void write_code(const char *var, int inactive = 0);
+ void write_inline(int inactive = 0);
const char *name() const {return name_;}
};
diff --git fluid/alignment_panel.cxx fluid/alignment_panel.cxx
index 532cf10..3a76c40 100644
--- fluid/alignment_panel.cxx
+++ fluid/alignment_panel.cxx
@@ -49,21 +49,26 @@ Fl_Menu_Item menu_i18n_type_chooser[] = {
Fl_Input *i18n_include_input=(Fl_Input *)0;
+Fl_Input *i18n_conditional_input=(Fl_Input *)0;
+
Fl_Input *i18n_file_input=(Fl_Input *)0;
Fl_Int_Input *i18n_set_input=(Fl_Int_Input *)0;
Fl_Input *i18n_function_input=(Fl_Input *)0;
+Fl_Input *i18n_static_function_input=(Fl_Input *)0;
+
Fl_Double_Window* make_project_window() {
{ project_window = new Fl_Double_Window(399, 275, "Project Settings");
{ Fl_Button* o = new Fl_Button(328, 239, 60, 25, "Close");
o->tooltip("Close this dialog.");
o->callback((Fl_Callback*)cb_Close);
} // Fl_Button* o
- { Fl_Tabs* o = new Fl_Tabs(10, 10, 378, 218);
+ { Fl_Tabs* o = new Fl_Tabs(10, 10, 379, 218);
o->selection_color((Fl_Color)12);
- { Fl_Group* o = new Fl_Group(10, 36, 378, 192, "Output");
+ o->labelcolor(FL_BACKGROUND2_COLOR);
+ { Fl_Group* o = new Fl_Group(10, 36, 379, 192, "Output");
{ Fl_Box* o = new Fl_Box(20, 49, 340, 49, "Use \"name.ext\" to set a file name or just \".ext\" to set extension.");
o->align(Fl_Align(132|FL_ALIGN_INSIDE));
} // Fl_Box* o
@@ -89,7 +94,7 @@ Fl_Double_Window* make_project_window() {
include_H_from_C_button->callback((Fl_Callback*)include_H_from_C_button_cb);
} // Fl_Check_Button* include_H_from_C_button
{ use_FL_COMMAND_button = new Fl_Check_Button(117, 176, 272, 20, "Menu shortcuts use FL_COMMAND");
- use_FL_COMMAND_button->tooltip("Replace FL_CTRL with FL_COMMAND when generating menu shortcut code.");
+ use_FL_COMMAND_button->tooltip("Replace FL_CTRL and FL_META with FL_COMMAND when generating menu shortcuts");
use_FL_COMMAND_button->down_box(FL_DOWN_BOX);
use_FL_COMMAND_button->callback((Fl_Callback*)use_FL_COMMAND_button_cb);
} // Fl_Check_Button* use_FL_COMMAND_button
@@ -104,7 +109,7 @@ ped using octal notation `\\0123`. If this option is checked, Fluid will write\
} // Fl_Group* o
{ Fl_Group* o = new Fl_Group(10, 36, 378, 192, "Internationalization");
o->hide();
- { i18n_type_chooser = new Fl_Choice(100, 48, 136, 25, "Use:");
+ { i18n_type_chooser = new Fl_Choice(128, 48, 136, 25, "Use:");
i18n_type_chooser->tooltip("Type of internationalization to use.");
i18n_type_chooser->box(FL_THIN_UP_BOX);
i18n_type_chooser->down_box(FL_BORDER_BOX);
@@ -112,21 +117,29 @@ ped using octal notation `\\0123`. If this option is checked, Fluid will write\
i18n_type_chooser->callback((Fl_Callback*)i18n_type_cb);
i18n_type_chooser->menu(menu_i18n_type_chooser);
} // Fl_Choice* i18n_type_chooser
- { i18n_include_input = new Fl_Input(100, 78, 272, 20, "#include:");
+ { i18n_include_input = new Fl_Input(128, 78, 243, 20, "#include:");
i18n_include_input->tooltip("The include file for internationalization.");
i18n_include_input->box(FL_THIN_DOWN_BOX);
i18n_include_input->labelfont(1);
i18n_include_input->textfont(4);
i18n_include_input->callback((Fl_Callback*)i18n_text_cb);
} // Fl_Input* i18n_include_input
- { i18n_file_input = new Fl_Input(100, 104, 272, 20, "File:");
+ { i18n_conditional_input = new Fl_Input(128, 103, 243, 20, "Conditional:");
+ i18n_conditional_input->tooltip("only include the header file if this preprocessor macro is defined, for examp\
+le FLTK_GETTEXT_FOUND");
+ i18n_conditional_input->box(FL_THIN_DOWN_BOX);
+ i18n_conditional_input->labelfont(1);
+ i18n_conditional_input->textfont(4);
+ i18n_conditional_input->callback((Fl_Callback*)i18n_text_cb);
+ } // Fl_Input* i18n_conditional_input
+ { i18n_file_input = new Fl_Input(128, 128, 243, 20, "File:");
i18n_file_input->tooltip("The name of the message catalog.");
i18n_file_input->box(FL_THIN_DOWN_BOX);
i18n_file_input->labelfont(1);
i18n_file_input->textfont(4);
i18n_file_input->callback((Fl_Callback*)i18n_text_cb);
} // Fl_Input* i18n_file_input
- { i18n_set_input = new Fl_Int_Input(100, 128, 272, 20, "Set:");
+ { i18n_set_input = new Fl_Int_Input(128, 153, 243, 20, "Set:");
i18n_set_input->tooltip("The message set number.");
i18n_set_input->type(2);
i18n_set_input->box(FL_THIN_DOWN_BOX);
@@ -134,13 +147,22 @@ ped using octal notation `\\0123`. If this option is checked, Fluid will write\
i18n_set_input->textfont(4);
i18n_set_input->callback((Fl_Callback*)i18n_int_cb);
} // Fl_Int_Input* i18n_set_input
- { i18n_function_input = new Fl_Input(100, 103, 272, 20, "Function:");
- i18n_function_input->tooltip("The function to call to internationalize the labels and tooltips.");
+ { i18n_function_input = new Fl_Input(128, 128, 243, 20, "Function:");
+ i18n_function_input->tooltip("The function to call to translate labels and tooltips, usually \"gettext\" or\
+ \"_\"");
i18n_function_input->box(FL_THIN_DOWN_BOX);
i18n_function_input->labelfont(1);
i18n_function_input->textfont(4);
i18n_function_input->callback((Fl_Callback*)i18n_text_cb);
} // Fl_Input* i18n_function_input
+ { i18n_static_function_input = new Fl_Input(128, 153, 243, 20, "Static Function:");
+ i18n_static_function_input->tooltip("function to call to translate static text, The function to call to internatio\
+nalize labels and tooltips, usually \"gettext_noop\" or \"N_\"");
+ i18n_static_function_input->box(FL_THIN_DOWN_BOX);
+ i18n_static_function_input->labelfont(1);
+ i18n_static_function_input->textfont(4);
+ i18n_static_function_input->callback((Fl_Callback*)i18n_text_cb);
+ } // Fl_Input* i18n_static_function_input
o->end();
} // Fl_Group* o
o->end();
@@ -525,7 +547,7 @@ Fl_Double_Window* make_layout_window() {
o->labelfont(1);
o->align(Fl_Align(FL_ALIGN_RIGHT|FL_ALIGN_INSIDE));
} // Fl_Box* o
- { Fl_Group* o = new Fl_Group(105, 115, 170, 75);
+ { Fl_Group* o = new Fl_Group(105, 115, 192, 75);
{ def_widget_size[0] = new Fl_Round_Button(115, 115, 70, 25);
def_widget_size[0]->type(102);
def_widget_size[0]->down_box(FL_ROUND_DOWN_BOX);
diff --git fluid/alignment_panel.fl fluid/alignment_panel.fl
index 0bce368..ca8fb6c 100644
--- fluid/alignment_panel.fl
+++ fluid/alignment_panel.fl
@@ -54,7 +54,7 @@ Function {make_project_window()} {open
} {
Fl_Window project_window {
label {Project Settings} open
- xywh {473 246 399 275} type Double
+ xywh {472 246 399 275} type Double
code0 {\#include <FL/Fl_Preferences.H>}
code1 {\#include <FL/Fl_Tooltip.H>} modal visible
} {
@@ -65,11 +65,11 @@ set_modflag(-1, -1);}
tooltip {Close this dialog.} xywh {328 239 60 25}
}
Fl_Tabs {} {open
- xywh {10 10 378 218} selection_color 12
+ xywh {10 10 379 218} selection_color 12 labelcolor 7
} {
Fl_Group {} {
- label Output open
- xywh {10 36 378 192}
+ label Output open selected
+ xywh {10 36 379 192}
} {
Fl_Box {} {
label {Use "name.ext" to set a file name or just ".ext" to set extension.}
@@ -95,11 +95,11 @@ set_modflag(-1, -1);}
Fl_Check_Button use_FL_COMMAND_button {
label {Menu shortcuts use FL_COMMAND}
callback use_FL_COMMAND_button_cb
- tooltip {Replace FL_CTRL with FL_COMMAND when generating menu shortcut code.} xywh {117 176 272 20} down_box DOWN_BOX
+ tooltip {Replace FL_CTRL and FL_META with FL_COMMAND when generating menu shortcuts} xywh {117 176 272 20} down_box DOWN_BOX
}
Fl_Check_Button utf8_in_src_button {
label {allow Unicode UTF-8 in source code}
- callback utf8_in_src_cb selected
+ callback utf8_in_src_cb
tooltip {For older compilers, characters outside of the printable ASCII range are escaped using octal notation `\\0123`. If this option is checked, Fluid will write UTF-8 characters unchanged.} xywh {117 199 272 20} down_box DOWN_BOX
}
}
@@ -110,7 +110,7 @@ set_modflag(-1, -1);}
Fl_Choice i18n_type_chooser {
label {Use:}
callback i18n_type_cb open
- tooltip {Type of internationalization to use.} xywh {100 48 136 25} box THIN_UP_BOX down_box BORDER_BOX labelfont 1
+ tooltip {Type of internationalization to use.} xywh {128 48 136 25} box THIN_UP_BOX down_box BORDER_BOX labelfont 1
} {
MenuItem {} {
label None
@@ -128,22 +128,32 @@ set_modflag(-1, -1);}
Fl_Input i18n_include_input {
label {\#include:}
callback i18n_text_cb
- tooltip {The include file for internationalization.} xywh {100 78 272 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+ tooltip {The include file for internationalization.} xywh {128 78 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+ }
+ Fl_Input i18n_conditional_input {
+ label {Conditional:}
+ callback i18n_text_cb
+ tooltip {only include the header file if this preprocessor macro is defined, for example FLTK_GETTEXT_FOUND} xywh {128 103 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
}
Fl_Input i18n_file_input {
label {File:}
callback i18n_text_cb
- tooltip {The name of the message catalog.} xywh {100 104 272 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+ tooltip {The name of the message catalog.} xywh {128 128 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
}
Fl_Input i18n_set_input {
label {Set:}
callback i18n_int_cb
- tooltip {The message set number.} xywh {100 128 272 20} type Int box THIN_DOWN_BOX labelfont 1 textfont 4
+ tooltip {The message set number.} xywh {128 153 243 20} type Int box THIN_DOWN_BOX labelfont 1 textfont 4
}
Fl_Input i18n_function_input {
label {Function:}
callback i18n_text_cb
- tooltip {The function to call to internationalize the labels and tooltips.} xywh {100 103 272 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+ tooltip {The function to call to translate labels and tooltips, usually "gettext" or "_"} xywh {128 128 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+ }
+ Fl_Input i18n_static_function_input {
+ label {Static Function:}
+ callback i18n_text_cb
+ tooltip {function to call to translate static text, The function to call to internationalize labels and tooltips, usually "gettext_noop" or "N_"} xywh {128 153 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
}
}
}
@@ -388,7 +398,7 @@ Function {make_layout_window()} {open
} {
Fl_Window grid_window {
label {Layout Settings}
- xywh {885 274 310 245} type Double non_modal visible
+ xywh {745 303 310 245} type Double non_modal visible
} {
Fl_Input horizontal_input {
label x
@@ -431,7 +441,7 @@ Function {make_layout_window()} {open
xywh {10 115 107 25} labelfont 1 align 24
}
Fl_Group {} {open
- xywh {105 115 170 75}
+ xywh {105 115 192 75}
} {
Fl_Round_Button {def_widget_size[0]} {
user_data 8 user_data_type long
diff --git fluid/alignment_panel.h fluid/alignment_panel.h
index a7cc711..9a6e609 100644
--- fluid/alignment_panel.h
+++ fluid/alignment_panel.h
@@ -51,11 +51,13 @@ extern void i18n_type_cb(Fl_Choice*, void*);
extern Fl_Choice *i18n_type_chooser;
extern void i18n_text_cb(Fl_Input*, void*);
extern Fl_Input *i18n_include_input;
+extern Fl_Input *i18n_conditional_input;
extern Fl_Input *i18n_file_input;
#include <FL/Fl_Int_Input.H>
extern void i18n_int_cb(Fl_Int_Input*, void*);
extern Fl_Int_Input *i18n_set_input;
extern Fl_Input *i18n_function_input;
+extern Fl_Input *i18n_static_function_input;
Fl_Double_Window* make_project_window();
extern Fl_Menu_Item menu_i18n_type_chooser[];
extern void i18n_cb(Fl_Choice *,void *);
diff --git fluid/code.cxx fluid/code.cxx
index e9af1fd..d6b4bdc 100644
--- fluid/code.cxx
+++ fluid/code.cxx
@@ -573,28 +573,55 @@ int write_code(const char *s, const char *t) {
}
write_declare("#include <FL/Fl.H>");
+ if (t && include_H_from_C) {
+ if (*header_file_name == '.' && strchr(header_file_name, '/') == NULL) {
+ write_c("#include \"%s\"\n", fl_filename_name(t));
+ } else {
+ write_c("#include \"%s\"\n", t);
+ }
+ }
if (i18n_type && i18n_include[0]) {
+ int conditional = (i18n_conditional[0]!=0);
+ if (conditional) {
+ write_c("#ifdef %s\n", i18n_conditional);
+ indentation++;
+ }
if (i18n_include[0] != '<' &&
i18n_include[0] != '\"')
- write_c("#include \"%s\"\n", i18n_include);
+ write_c("#%sinclude \"%s\"\n", indent(), i18n_include);
else
- write_c("#include %s\n", i18n_include);
+ write_c("#%sinclude %s\n", indent(), i18n_include);
if (i18n_type == 2) {
if (i18n_file[0]) write_c("extern nl_catd %s;\n", i18n_file);
else {
write_c("// Initialize I18N stuff now for menus...\n");
- write_c("#include <locale.h>\n");
+ write_c("#%sinclude <locale.h>\n", indent());
write_c("static char *_locale = setlocale(LC_MESSAGES, \"\");\n");
write_c("static nl_catd _catalog = catopen(\"%s\", 0);\n",
i18n_program);
}
}
- }
- if (t && include_H_from_C) {
- if (*header_file_name == '.' && strchr(header_file_name, '/') == NULL) {
- write_c("#include \"%s\"\n", fl_filename_name(t));
- } else {
- write_c("#include \"%s\"\n", t);
+ if (conditional) {
+ write_c("#else\n");
+ if (i18n_type == 1) {
+ if (i18n_function[0]) {
+ write_c("#%sifndef %s\n", indent(), i18n_function);
+ write_c("#%sdefine %s(text) text\n", indent_plus(1), i18n_function);
+ write_c("#%sendif\n", indent());
+ }
+ }
+ if (i18n_type == 2) {
+ write_c("#%sifndef catgets\n", indent());
+ write_c("#%sdefine catgets(catalog, set, msgid, text) text\n", indent_plus(1));
+ write_c("#%sendif\n", indent());
+ }
+ indentation--;
+ write_c("#endif\n");
+ }
+ if (i18n_type == 1 && i18n_static_function[0]) {
+ write_c("#ifndef %s\n", i18n_static_function);
+ write_c("#%sdefine %s(text) text\n", indent_plus(1), i18n_static_function);
+ write_c("#endif\n");
}
}
for (Fl_Type* p = first_type; p;) {
diff --git fluid/file.cxx fluid/file.cxx
index b38be1c..044e4b1 100644
--- fluid/file.cxx
+++ fluid/file.cxx
@@ -385,14 +385,16 @@ int write_file(const char *filename, int selected_only) {
write_string("\nutf8_in_src");
if (i18n_type) {
write_string("\ni18n_type %d", i18n_type);
- write_string("\ni18n_include %s", i18n_include);
+ write_string("\ni18n_include"); write_word(i18n_include);
+ write_string("\ni18n_conditional"); write_word(i18n_conditional);
switch (i18n_type) {
case 1 : /* GNU gettext */
- write_string("\ni18n_function %s", i18n_function);
+ write_string("\ni18n_function"); write_word(i18n_function);
+ write_string("\ni18n_static_function"); write_word(i18n_static_function);
break;
case 2 : /* POSIX catgets */
- if (i18n_file[0]) write_string("\ni18n_file %s", i18n_file);
- write_string("\ni18n_set %s", i18n_set);
+ if (i18n_file[0]) write_string("\ni18n_file"); write_word(i18n_file);
+ write_string("\ni18n_set"); write_word(i18n_set);
break;
}
}
@@ -507,6 +509,10 @@ static void read_children(Fl_Type *p, int paste, Strategy strategy, char skip_op
i18n_function = fl_strdup(read_word());
goto CONTINUE;
}
+ if (!strcmp(c,"i18n_static_function")) {
+ i18n_static_function = fl_strdup(read_word());
+ goto CONTINUE;
+ }
if (!strcmp(c,"i18n_file")) {
i18n_file = fl_strdup(read_word());
goto CONTINUE;
@@ -519,9 +525,8 @@ static void read_children(Fl_Type *p, int paste, Strategy strategy, char skip_op
i18n_include = fl_strdup(read_word());
goto CONTINUE;
}
- if (!strcmp(c,"i18n_type"))
- {
- i18n_type = atoi(read_word());
+ if (!strcmp(c,"i18n_conditional")) {
+ i18n_conditional = fl_strdup(read_word());
goto CONTINUE;
}
if (!strcmp(c,"i18n_type"))
diff --git fluid/fluid.cxx fluid/fluid.cxx
index df8a713..95568d6 100644
--- fluid/fluid.cxx
+++ fluid/fluid.cxx
@@ -180,14 +180,47 @@ const char* code_file_name = ".cxx";
/// \todo document me
int i18n_type = 0;
-/// Saved in the .fl design file.
-/// \todo document me
+/**
+ For either type of translation, write a #include statement into the
+ source file.
+
+ This is usually `<libintl.h>` or `"gettext.h"` for GNU gettext, or
+ `<nl_types.h>` for Posix catgets.
+
+ Fluid accepts filenames in quotes or in \< and \>. If neither is found,
+ double quotes are added.
+ If this value is emty, no include statement will be generated.
+
+ Saved in the .fl design file.
+ */
const char* i18n_include = "";
-/// Saved in the .fl design file.
-/// \todo document me
+const char* i18n_conditional = "";
+
+/**
+ For the gettext/intl.h options, this is the function that translates text
+ at runtime.
+
+ This is usually "gettext" or "_".
+ This should not be empty.
+ Saved in the .fl design file.
+
+ */
const char* i18n_function = "";
+/**
+ For the gettext/intl.h options, this is the function that marks the
+ translation of text at initialisation time.
+
+ This is usually "gettext_noop" or "N_".
+ This should not be empty.
+ Fluid will translate static text (usually in menu items) later when used
+ for the first time.
+
+ Saved in the .fl design file.
+ */
+const char* i18n_static_function = "";
+
/// Saved in the .fl design file.
/// \todo document me
const char* i18n_file = "";
diff --git fluid/fluid.h fluid/fluid.h
index ff30984..a00a775 100644
--- fluid/fluid.h
+++ fluid/fluid.h
@@ -82,7 +82,9 @@ extern const char* code_file_name;
extern int i18n_type;
extern const char* i18n_include;
+extern const char* i18n_conditional;
extern const char* i18n_function;
+extern const char* i18n_static_function;
extern const char* i18n_file;
extern const char* i18n_set;;
extern char i18n_program[FL_PATH_MAX];
diff --git test/preferences.fl test/preferences.fl
index b28e0c0..d90119f 100644
--- test/preferences.fl
+++ test/preferences.fl
@@ -1,5 +1,10 @@
# data file for the Fltk User Interface Designer (fluid)
version 1.0400
+i18n_type 1
+i18n_include {<libintl.h>}
+i18n_conditional FLTK_GETTEXT_FOUND
+i18n_function gettext
+i18n_static_function gettext_noop
header_name {.h}
code_name {.cxx}
decl {\#include <FL/Fl_Preferences.H>} {public local
@@ -45,7 +50,7 @@ Function {} {open return_type int
Fl_Window myWindow {
label {My Preferences}
callback closeWindowCB open
- xywh {586 277 298 311} type Double visible
+ xywh {585 277 298 311} type Double visible
} {
Fl_Button {} {
label Cancel
@@ -62,7 +67,7 @@ Function {} {open return_type int
xywh {20 30 115 225} box ENGRAVED_FRAME align 5
} {
Fl_Input wAlarm {
- label {Alarm at:} selected
+ label {Alarm at:}
xywh {25 55 45 20} align 5
}
Fl_Choice wAmPm {open
@@ -73,7 +78,7 @@ Function {} {open return_type int
xywh {0 0 100 20}
}
MenuItem {} {
- label {p.m.}
+ label {p.m.} selected
xywh {0 0 100 20}
}
}
[ Direct Link to Message ] | |