Fluid: last incremental change: restructuring
This commit is contained in:
parent
187eaa0441
commit
ca22660bbb
2340
fluid/app/project.cxx
Normal file
2340
fluid/app/project.cxx
Normal file
File diff suppressed because it is too large
Load Diff
209
fluid/app/project.h
Normal file
209
fluid/app/project.h
Normal file
@ -0,0 +1,209 @@
|
||||
//
|
||||
// FLUID main entry for the Fast Light Tool Kit (FLTK).
|
||||
//
|
||||
// Copyright 1998-2025 by Bill Spitzak and others.
|
||||
//
|
||||
// This library is free software. Distribution and use rights are outlined in
|
||||
// the file "COPYING" which should have been included with this file. If this
|
||||
// file is missing or damaged, see the license at:
|
||||
//
|
||||
// https://www.fltk.org/COPYING.php
|
||||
//
|
||||
// Please see the following page on how to report bugs and issues:
|
||||
//
|
||||
// https://www.fltk.org/bugs.php
|
||||
//
|
||||
|
||||
#ifndef _FLUID_FLUID_H
|
||||
#define _FLUID_FLUID_H
|
||||
|
||||
#include "tools/filename.h"
|
||||
|
||||
#include <FL/Fl_Preferences.H>
|
||||
#include <FL/Fl_Menu_Item.H>
|
||||
#include <FL/filename.H>
|
||||
|
||||
#include <string>
|
||||
|
||||
#define BROWSERWIDTH 300
|
||||
#define BROWSERHEIGHT 500
|
||||
#define WINWIDTH 300
|
||||
#define MENUHEIGHT 25
|
||||
#define WINHEIGHT (BROWSERHEIGHT+MENUHEIGHT)
|
||||
|
||||
// ---- types
|
||||
|
||||
class Fl_Double_Window;
|
||||
class Fl_Window;
|
||||
class Fl_Menu_Bar;
|
||||
class Fl_Type;
|
||||
class Fl_Choice;
|
||||
class Fl_Button;
|
||||
class Fl_Check_Button;
|
||||
|
||||
/**
|
||||
Indicate the storage location for tools like layout suites and shell macros.
|
||||
\see class Fd_Shell_Command, class Fd_Layout_Suite
|
||||
*/
|
||||
typedef enum {
|
||||
FD_STORE_INTERNAL, ///< stored inside FLUID app
|
||||
FD_STORE_USER, ///< suite is stored in the user wide FLUID settings
|
||||
FD_STORE_PROJECT, ///< suite is stored within the current .fl project file
|
||||
FD_STORE_FILE ///< store suite in external file
|
||||
} Fd_Tool_Store;
|
||||
|
||||
// ---- global variables
|
||||
|
||||
extern Fl_Preferences fluid_prefs;
|
||||
extern Fl_Menu_Item Main_Menu[];
|
||||
extern Fl_Menu_Bar *main_menubar;
|
||||
extern Fl_Window *main_window;
|
||||
|
||||
extern int show_guides;
|
||||
extern int show_restricted;
|
||||
extern int show_ghosted_outline;
|
||||
extern int show_comments;
|
||||
|
||||
extern int G_use_external_editor;
|
||||
extern int G_debug;
|
||||
extern char G_external_editor_command[512];
|
||||
|
||||
// File history info...
|
||||
extern char absolute_history[10][FL_PATH_MAX];
|
||||
extern char relative_history[10][FL_PATH_MAX];
|
||||
extern void load_history();
|
||||
extern void update_history(const char *);
|
||||
|
||||
extern Fl_Menu_Item *save_item;
|
||||
extern Fl_Menu_Item *history_item;
|
||||
extern Fl_Menu_Item *widgetbin_item;
|
||||
extern Fl_Menu_Item *codeview_item;
|
||||
extern Fl_Menu_Item *overlay_item;
|
||||
extern Fl_Button *overlay_button;
|
||||
extern Fl_Menu_Item *guides_item;
|
||||
extern Fl_Menu_Item *restricted_item;
|
||||
extern Fl_Check_Button *guides_button;
|
||||
|
||||
extern int modflag;
|
||||
|
||||
extern int update_file; // fluid -u
|
||||
extern int compile_file; // fluid -c
|
||||
extern int compile_strings; // fluic -cs
|
||||
extern int batch_mode;
|
||||
|
||||
extern int pasteoffset;
|
||||
|
||||
extern std::string g_code_filename_arg;
|
||||
extern std::string g_header_filename_arg;
|
||||
extern std::string g_launch_path;
|
||||
|
||||
extern std::string g_autodoc_path;
|
||||
|
||||
// ---- project class declaration
|
||||
|
||||
/**
|
||||
Enumeration of available internationalization types.
|
||||
*/
|
||||
typedef enum {
|
||||
FD_I18N_NONE = 0, ///< No i18n, all strings are litearals
|
||||
FD_I18N_GNU, ///< GNU gettext internationalization
|
||||
FD_I18N_POSIX ///< Posix catgets internationalization
|
||||
} Fd_I18n_Type;
|
||||
|
||||
/**
|
||||
Data and settings for a FLUID project file.
|
||||
*/
|
||||
class Fluid_Project {
|
||||
public:
|
||||
Fluid_Project();
|
||||
~Fluid_Project();
|
||||
void reset();
|
||||
void update_settings_dialog();
|
||||
|
||||
std::string projectfile_path() const;
|
||||
std::string projectfile_name() const;
|
||||
std::string codefile_path() const;
|
||||
std::string codefile_name() const;
|
||||
std::string headerfile_path() const;
|
||||
std::string headerfile_name() const;
|
||||
std::string stringsfile_path() const;
|
||||
std::string stringsfile_name() const;
|
||||
std::string basename() const;
|
||||
|
||||
/// One of the available internationalization types.
|
||||
Fd_I18n_Type i18n_type;
|
||||
/// Include file for GNU i18n, writes an #include statement into the source
|
||||
/// file. This is usually `<libintl.h>` or `"gettext.h"` for GNU gettext.
|
||||
std::string i18n_gnu_include;
|
||||
// Optional name of a macro for conditional i18n compilation.
|
||||
std::string i18n_gnu_conditional;
|
||||
/// For the gettext/intl.h options, this is the function that translates text
|
||||
/// at runtime. This is usually "gettext" or "_".
|
||||
std::string i18n_gnu_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_".
|
||||
std::string i18n_gnu_static_function;
|
||||
|
||||
/// Include file for Posix i18n, write a #include statement into the source
|
||||
/// file. This is usually `<nl_types.h>` for Posix catgets.
|
||||
std::string i18n_pos_include;
|
||||
// Optional name of a macro for conditional i18n compilation.
|
||||
std::string i18n_pos_conditional;
|
||||
/// Name of the nl_catd database
|
||||
std::string i18n_pos_file;
|
||||
/// Message set ID for the catalog.
|
||||
std::string i18n_pos_set;
|
||||
|
||||
/// If set, generate code to include the header file form the c++ file
|
||||
int include_H_from_C;
|
||||
/// If set, handle keyboard shortcut Ctrl on macOS using Cmd instead
|
||||
int use_FL_COMMAND;
|
||||
/// Clear if UTF-8 characters in statics texts are written as escape sequences
|
||||
int utf8_in_src;
|
||||
/// If set, <FL/Fl.H> will not be included from the header code before anything else
|
||||
int avoid_early_includes;
|
||||
/// If set, command line overrides header file name in .fl file.
|
||||
int header_file_set;
|
||||
/// If set, command line overrides source code file name in .fl file.
|
||||
int code_file_set;
|
||||
int write_mergeback_data;
|
||||
/// Hold the default extension for header files, or the entire filename if set via command line.
|
||||
std::string header_file_name;
|
||||
/// Hold the default extension for source code files, or the entire filename if set via command line.
|
||||
std::string code_file_name;
|
||||
};
|
||||
|
||||
extern Fluid_Project g_project;
|
||||
|
||||
// ---- public functions
|
||||
|
||||
extern int fluid_main(int argc,char **argv);
|
||||
|
||||
extern bool new_project(bool user_must_confirm = true);
|
||||
extern void enter_project_dir();
|
||||
extern void leave_project_dir();
|
||||
extern void set_filename(const char *c);
|
||||
extern void set_modflag(int mf, int mfc=-1);
|
||||
|
||||
extern const std::string &get_tmpdir();
|
||||
|
||||
// ---- public callback functions
|
||||
|
||||
extern void save_cb(Fl_Widget *, void *v);
|
||||
extern void save_template_cb(Fl_Widget *, void *);
|
||||
extern void revert_cb(Fl_Widget *,void *);
|
||||
extern void exit_cb(Fl_Widget *,void *);
|
||||
|
||||
extern int write_code_files(bool dont_show_completion_dialog=false);
|
||||
extern void write_strings_cb(Fl_Widget *, void *);
|
||||
extern void align_widget_cb(Fl_Widget *, long);
|
||||
extern void toggle_widgetbin_cb(Fl_Widget *, void *);
|
||||
|
||||
extern char position_window(Fl_Window *w, const char *prefsName, int Visible, int X, int Y, int W=0, int H=0);
|
||||
|
||||
inline int fd_min(int a, int b) { return (a < b ? a : b); }
|
||||
inline int fd_max(int a, int b) { return (a > b ? a : b); }
|
||||
inline int fd_min(int a, int b, int c) { return fd_min(a, fd_min(b, c)); }
|
||||
|
||||
#endif // _FLUID_FLUID_H
|
||||
|
||||
983
fluid/io/Project_Writer.cxx
Normal file
983
fluid/io/Project_Writer.cxx
Normal file
@ -0,0 +1,983 @@
|
||||
//
|
||||
// Fluid file routines for the Fast Light Tool Kit (FLTK).
|
||||
//
|
||||
// You may find the basic read_* and write_* routines to
|
||||
// be useful for other programs. I have used them many times.
|
||||
// They are somewhat similar to tcl, using matching { and }
|
||||
// to quote strings.
|
||||
//
|
||||
// Copyright 1998-2023 by Bill Spitzak and others.
|
||||
//
|
||||
// This library is free software. Distribution and use rights are outlined in
|
||||
// the file "COPYING" which should have been included with this file. If this
|
||||
// file is missing or damaged, see the license at:
|
||||
//
|
||||
// https://www.fltk.org/COPYING.php
|
||||
//
|
||||
// Please see the following page on how to report bugs and issues:
|
||||
//
|
||||
// https://www.fltk.org/bugs.php
|
||||
//
|
||||
|
||||
#include "io/file.h"
|
||||
|
||||
#include "app/fluid.h"
|
||||
#include "app/shell_command.h"
|
||||
#include "app/undo.h"
|
||||
#include "io/code.h"
|
||||
#include "nodes/factory.h"
|
||||
#include "nodes/Fl_Function_Type.h"
|
||||
#include "nodes/Fl_Widget_Type.h"
|
||||
#include "nodes/Fl_Grid_Type.h"
|
||||
#include "nodes/Fl_Window_Type.h"
|
||||
#include "panels/settings_panel.h"
|
||||
#include "widgets/Node_Browser.h"
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Group.H>
|
||||
#include <FL/fl_string_functions.h>
|
||||
#include <FL/fl_message.H>
|
||||
#include "../src/flstring.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/// \defgroup flfile .fl Project File Operations
|
||||
/// \{
|
||||
|
||||
// This file contains code to read and write .fl files.
|
||||
|
||||
/// If set, we read an old fdesign file and widget y coordinates need to be flipped.
|
||||
int fdesign_flip = 0;
|
||||
|
||||
/** \brief Read a .fl project file.
|
||||
|
||||
The .fl file format is documented in `fluid/README_fl.txt`.
|
||||
|
||||
\param[in] filename read this file
|
||||
\param[in] merge if this is set, merge the file into an existing project
|
||||
at Fl_Type::current
|
||||
\param[in] strategy add new nodes after current or as last child
|
||||
\return 0 if the operation failed, 1 if it succeeded
|
||||
*/
|
||||
int read_file(const char *filename, int merge, Strategy strategy) {
|
||||
Fd_Project_Reader f;
|
||||
strategy.source(Strategy::FROM_FILE);
|
||||
return f.read_project(filename, merge, strategy);
|
||||
}
|
||||
|
||||
/** \brief Write an .fl design description file.
|
||||
|
||||
The .fl file format is documented in `fluid/README_fl.txt`.
|
||||
|
||||
\param[in] filename create this file, and if it exists, overwrite it
|
||||
\param[in] selected_only write only the selected nodes in the widget_tree. This
|
||||
is used to implement copy and paste.
|
||||
\return 0 if the operation failed, 1 if it succeeded
|
||||
*/
|
||||
int write_file(const char *filename, int selected_only, bool to_codeview) {
|
||||
Fd_Project_Writer out;
|
||||
return out.write_project(filename, selected_only, to_codeview);
|
||||
}
|
||||
|
||||
/**
|
||||
Convert a single ASCII char, assumed to be a hex digit, into its decimal value.
|
||||
\param[in] x ASCII character
|
||||
\return decimal value or 20 if character is not a valid hex digit (0..9,a..f,A..F)
|
||||
*/
|
||||
static int hexdigit(int x) {
|
||||
if ((x < 0) || (x > 127)) return 20;
|
||||
if (isdigit(x)) return x-'0';
|
||||
if (isupper(x)) return x-'A'+10;
|
||||
if (islower(x)) return x-'a'+10;
|
||||
return 20;
|
||||
}
|
||||
|
||||
// ---- Fd_Project_Reader ---------------------------------------------- MARK: -
|
||||
|
||||
/**
|
||||
A simple growing buffer.
|
||||
Oh how I wish sometimes we would upgrade to modern C++.
|
||||
\param[in] length minimum length in bytes
|
||||
*/
|
||||
void Fd_Project_Reader::expand_buffer(int length) {
|
||||
if (length >= buflen) {
|
||||
if (!buflen) {
|
||||
buflen = length+1;
|
||||
buffer = (char*)malloc(buflen);
|
||||
} else {
|
||||
buflen = 2*buflen;
|
||||
if (length >= buflen) buflen = length+1;
|
||||
buffer = (char *)realloc((void *)buffer,buflen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Construct local project reader. */
|
||||
Fd_Project_Reader::Fd_Project_Reader()
|
||||
: fin(NULL),
|
||||
lineno(0),
|
||||
fname(NULL),
|
||||
buffer(NULL),
|
||||
buflen(0),
|
||||
read_version(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
/** \brief Release project reader resources. */
|
||||
Fd_Project_Reader::~Fd_Project_Reader()
|
||||
{
|
||||
// fname is not copied, so do not free it
|
||||
if (buffer)
|
||||
::free(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
Open an .fl file for reading.
|
||||
\param[in] s filename, if NULL, read from stdin instead
|
||||
\return 0 if the operation failed, 1 if it succeeded
|
||||
*/
|
||||
int Fd_Project_Reader::open_read(const char *s) {
|
||||
lineno = 1;
|
||||
if (!s) {
|
||||
fin = stdin;
|
||||
fname = "stdin";
|
||||
} else {
|
||||
FILE *f = fl_fopen(s, "rb");
|
||||
if (!f)
|
||||
return 0;
|
||||
fin = f;
|
||||
fname = s;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
Close the .fl file.
|
||||
\return 0 if the operation failed, 1 if it succeeded
|
||||
*/
|
||||
int Fd_Project_Reader::close_read() {
|
||||
if (fin != stdin) {
|
||||
int x = fclose(fin);
|
||||
fin = 0;
|
||||
return x >= 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the name part of the current filename and path.
|
||||
\return a pointer into a string that is not owned by this class
|
||||
*/
|
||||
const char *Fd_Project_Reader::filename_name() {
|
||||
return fl_filename_name(fname);
|
||||
}
|
||||
|
||||
/**
|
||||
Convert an ASCII sequence from the \.fl file following a previously read `\\` into a single character.
|
||||
Conversion includes the common C style \\ characters like \\n, \\x## hex
|
||||
values, and \\o### octal values.
|
||||
\return a character in the ASCII range
|
||||
*/
|
||||
int Fd_Project_Reader::read_quoted() { // read whatever character is after a \ .
|
||||
int c,d,x;
|
||||
switch(c = nextchar()) {
|
||||
case '\n': lineno++; return -1;
|
||||
case 'a' : return('\a');
|
||||
case 'b' : return('\b');
|
||||
case 'f' : return('\f');
|
||||
case 'n' : return('\n');
|
||||
case 'r' : return('\r');
|
||||
case 't' : return('\t');
|
||||
case 'v' : return('\v');
|
||||
case 'x' : /* read hex */
|
||||
for (c=x=0; x<3; x++) {
|
||||
int ch = nextchar();
|
||||
d = hexdigit(ch);
|
||||
if (d > 15) {ungetc(ch,fin); break;}
|
||||
c = (c<<4)+d;
|
||||
}
|
||||
break;
|
||||
default: /* read octal */
|
||||
if (c<'0' || c>'7') break;
|
||||
c -= '0';
|
||||
for (x=0; x<2; x++) {
|
||||
int ch = nextchar();
|
||||
d = hexdigit(ch);
|
||||
if (d>7) {ungetc(ch,fin); break;}
|
||||
c = (c<<3)+d;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return(c);
|
||||
}
|
||||
|
||||
/**
|
||||
Recursively read child nodes in the .fl design file.
|
||||
|
||||
If this is the first call, also read the global settings for this design.
|
||||
|
||||
\param[in] p parent node or NULL
|
||||
\param[in] merge if set, merge into existing design, else replace design
|
||||
\param[in] strategy add nodes after current or as last child
|
||||
\param[in] skip_options this is set if the options were already found in
|
||||
a previous call, and there is no need to waste time searching for them.
|
||||
\return the last type that was created
|
||||
*/
|
||||
Fl_Type *Fd_Project_Reader::read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options) {
|
||||
Fl_Type::current = p;
|
||||
Fl_Type *last_child_read = NULL;
|
||||
Fl_Type *t = NULL;
|
||||
for (;;) {
|
||||
const char *c = read_word();
|
||||
REUSE_C:
|
||||
if (!c) {
|
||||
if (p && !merge)
|
||||
read_error("Missing '}'");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(c,"}")) {
|
||||
if (!p) read_error("Unexpected '}'");
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure that we don't go through the list of options for child nodes
|
||||
if (!skip_options) {
|
||||
// this is the first word in a .fd file:
|
||||
if (!strcmp(c,"Magic:")) {
|
||||
read_fdesign();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!strcmp(c,"version")) {
|
||||
c = read_word();
|
||||
read_version = strtod(c,0);
|
||||
if (read_version<=0 || read_version>double(FL_VERSION+0.00001))
|
||||
read_error("unknown version '%s'",c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// back compatibility with Vincent Penne's original class code:
|
||||
if (!p && !strcmp(c,"define_in_struct")) {
|
||||
Fl_Type *t = add_new_widget_from_file("class", Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
t->name(read_word());
|
||||
Fl_Type::current = p = t;
|
||||
merge = 1; // stops "missing }" error
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(c,"do_not_include_H_from_C")) {
|
||||
g_project.include_H_from_C=0;
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"use_FL_COMMAND")) {
|
||||
g_project.use_FL_COMMAND=1;
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"utf8_in_src")) {
|
||||
g_project.utf8_in_src=1;
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"avoid_early_includes")) {
|
||||
g_project.avoid_early_includes=1;
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"i18n_type")) {
|
||||
g_project.i18n_type = static_cast<Fd_I18n_Type>(atoi(read_word()));
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"i18n_gnu_function")) {
|
||||
g_project.i18n_gnu_function = read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"i18n_gnu_static_function")) {
|
||||
g_project.i18n_gnu_static_function = read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"i18n_pos_file")) {
|
||||
g_project.i18n_pos_file = read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"i18n_pos_set")) {
|
||||
g_project.i18n_pos_set = read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"i18n_include")) {
|
||||
if (g_project.i18n_type == FD_I18N_GNU)
|
||||
g_project.i18n_gnu_include = read_word();
|
||||
else if (g_project.i18n_type == FD_I18N_POSIX)
|
||||
g_project.i18n_pos_include = read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"i18n_conditional")) {
|
||||
if (g_project.i18n_type == FD_I18N_GNU)
|
||||
g_project.i18n_gnu_conditional = read_word();
|
||||
else if (g_project.i18n_type == FD_I18N_POSIX)
|
||||
g_project.i18n_pos_conditional = read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
if (!strcmp(c,"header_name")) {
|
||||
if (!g_project.header_file_set) g_project.header_file_name = read_word();
|
||||
else read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
if (!strcmp(c,"code_name")) {
|
||||
if (!g_project.code_file_set) g_project.code_file_name = read_word();
|
||||
else read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
if (!strcmp(c, "snap")) {
|
||||
g_layout_list.read(this);
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
if (!strcmp(c, "gridx") || !strcmp(c, "gridy")) {
|
||||
// grid settings are now global
|
||||
read_word();
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
if (strcmp(c, "shell_commands")==0) {
|
||||
if (g_shell_config) {
|
||||
g_shell_config->read(this);
|
||||
} else {
|
||||
read_word();
|
||||
}
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
if (!strcmp(c, "mergeback")) {
|
||||
g_project.write_mergeback_data = read_int();
|
||||
goto CONTINUE;
|
||||
}
|
||||
}
|
||||
t = add_new_widget_from_file(c, strategy);
|
||||
if (!t) {
|
||||
read_error("Unknown word \"%s\"", c);
|
||||
continue;
|
||||
}
|
||||
last_child_read = t;
|
||||
// After reading the first widget, we no longer need to look for options
|
||||
skip_options = 1;
|
||||
|
||||
t->name(read_word());
|
||||
|
||||
c = read_word(1);
|
||||
if (strcmp(c,"{") && t->is_class()) { // <prefix> <name>
|
||||
((Fl_Class_Type*)t)->prefix(t->name());
|
||||
t->name(c);
|
||||
c = read_word(1);
|
||||
}
|
||||
|
||||
if (strcmp(c,"{")) {
|
||||
read_error("Missing property list for %s\n",t->title());
|
||||
goto REUSE_C;
|
||||
}
|
||||
|
||||
t->folded_ = 1;
|
||||
for (;;) {
|
||||
const char *cc = read_word();
|
||||
if (!cc || !strcmp(cc,"}")) break;
|
||||
t->read_property(*this, cc);
|
||||
}
|
||||
|
||||
if (t->can_have_children()) {
|
||||
c = read_word(1);
|
||||
if (strcmp(c,"{")) {
|
||||
read_error("Missing child list for %s\n",t->title());
|
||||
goto REUSE_C;
|
||||
}
|
||||
read_children(t, 0, Strategy::FROM_FILE_AS_LAST_CHILD, skip_options);
|
||||
t->postprocess_read();
|
||||
// FIXME: this has no business in the file reader!
|
||||
// TODO: this is called whenever something is pasted from the top level into a grid
|
||||
// It makes sense to make this more universal for other widget types too.
|
||||
if (merge && t && t->parent && t->parent->is_a(ID_Grid)) {
|
||||
if (Fl_Window_Type::popupx != 0x7FFFFFFF) {
|
||||
((Fl_Grid_Type*)t->parent)->insert_child_at(((Fl_Widget_Type*)t)->o, Fl_Window_Type::popupx, Fl_Window_Type::popupy);
|
||||
} else {
|
||||
((Fl_Grid_Type*)t->parent)->insert_child_at_next_free_cell(((Fl_Widget_Type*)t)->o);
|
||||
}
|
||||
}
|
||||
|
||||
t->layout_widget();
|
||||
}
|
||||
|
||||
if (strategy.placement() == Strategy::AS_FIRST_CHILD) {
|
||||
strategy.placement(Strategy::AFTER_CURRENT);
|
||||
}
|
||||
if (strategy.placement() == Strategy::AFTER_CURRENT) {
|
||||
Fl_Type::current = t;
|
||||
} else {
|
||||
Fl_Type::current = p;
|
||||
}
|
||||
|
||||
CONTINUE:;
|
||||
}
|
||||
if (merge && last_child_read && last_child_read->parent) {
|
||||
last_child_read->parent->postprocess_read();
|
||||
last_child_read->parent->layout_widget();
|
||||
}
|
||||
return last_child_read;
|
||||
}
|
||||
|
||||
/** \brief Read a .fl project file.
|
||||
\param[in] filename read this file
|
||||
\param[in] merge if this is set, merge the file into an existing project
|
||||
at Fl_Type::current
|
||||
\param[in] strategy add new nodes after current or as last child
|
||||
\return 0 if the operation failed, 1 if it succeeded
|
||||
*/
|
||||
int Fd_Project_Reader::read_project(const char *filename, int merge, Strategy strategy) {
|
||||
Fl_Type *o;
|
||||
undo_suspend();
|
||||
read_version = 0.0;
|
||||
if (!open_read(filename)) {
|
||||
undo_resume();
|
||||
return 0;
|
||||
}
|
||||
if (merge)
|
||||
deselect();
|
||||
else
|
||||
g_project.reset();
|
||||
read_children(Fl_Type::current, merge, strategy);
|
||||
// clear this
|
||||
Fl_Type::current = 0;
|
||||
// Force menu items to be rebuilt...
|
||||
for (o = Fl_Type::first; o; o = o->next) {
|
||||
if (o->is_a(ID_Menu_Manager_)) {
|
||||
o->add_child(0,0);
|
||||
}
|
||||
}
|
||||
for (o = Fl_Type::first; o; o = o->next) {
|
||||
if (o->selected) {
|
||||
Fl_Type::current = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
selection_changed(Fl_Type::current);
|
||||
if (g_shell_config) {
|
||||
g_shell_config->rebuild_shell_menu();
|
||||
g_shell_config->update_settings_dialog();
|
||||
}
|
||||
g_layout_list.update_dialogs();
|
||||
g_project.update_settings_dialog();
|
||||
int ret = close_read();
|
||||
undo_resume();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
Display an error while reading the file.
|
||||
If the .fl file isn't opened for reading, pop up an FLTK dialog, otherwise
|
||||
print to stdout.
|
||||
\note Matt: I am not sure why it is done this way. Shouldn't this depend on \c batch_mode?
|
||||
\todo Not happy about this function. Output channel should depend on `batch_mode`
|
||||
as the note above already states. I want to make all file readers and writers
|
||||
depend on an error handling base class that outputs a useful analysis of file
|
||||
operations.
|
||||
\param[in] format printf style format string, followed by an argument list
|
||||
*/
|
||||
void Fd_Project_Reader::read_error(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (!fin) { // FIXME: this line suppresses any error messages in interactive mode
|
||||
char buffer[1024]; // TODO: hides class member "buffer"
|
||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
fl_message("%s", buffer);
|
||||
} else {
|
||||
fprintf(stderr, "%s:%d: ", fname, lineno);
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
Return a word read from the .fl file, or NULL at the EOF.
|
||||
|
||||
This will skip all comments (# to end of line), and evaluate
|
||||
all \\xxx sequences and use \\ at the end of line to remove the newline.
|
||||
|
||||
A word is any one of:
|
||||
- a continuous string of non-space chars except { and } and #
|
||||
- everything between matching {...} (unless wantbrace != 0)
|
||||
- the characters '{' and '}'
|
||||
|
||||
\param[in] wantbrace if set, reading a `{` as the first non-space character
|
||||
will return the string `"{"`, if clear, a `{` is seen as the start of a word
|
||||
\return a pointer to the internal buffer, containing a copy of the word.
|
||||
Don't free the buffer! Note that most (all?) other file operations will
|
||||
overwrite this buffer. If wantbrace is not set, but we read a leading '{',
|
||||
the returned string will be stripped of its leading and trailing braces.
|
||||
*/
|
||||
const char *Fd_Project_Reader::read_word(int wantbrace) {
|
||||
int x;
|
||||
|
||||
// skip all the whitespace before it:
|
||||
for (;;) {
|
||||
x = nextchar();
|
||||
if (x < 0 && feof(fin)) { // eof
|
||||
return 0;
|
||||
} else if (x == '#') { // comment
|
||||
do x = nextchar(); while (x >= 0 && x != '\n');
|
||||
lineno++;
|
||||
continue;
|
||||
} else if (x == '\n') {
|
||||
lineno++;
|
||||
} else if (!isspace(x & 255)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
expand_buffer(100);
|
||||
|
||||
if (x == '{' && !wantbrace) {
|
||||
|
||||
// read in whatever is between braces
|
||||
int length = 0;
|
||||
int nesting = 0;
|
||||
for (;;) {
|
||||
x = nextchar();
|
||||
if (x<0) {read_error("Missing '}'"); break;}
|
||||
else if (x == '#') { // embedded comment
|
||||
do x = nextchar(); while (x >= 0 && x != '\n');
|
||||
lineno++;
|
||||
continue;
|
||||
} else if (x == '\n') lineno++;
|
||||
else if (x == '\\') {x = read_quoted(); if (x<0) continue;}
|
||||
else if (x == '{') nesting++;
|
||||
else if (x == '}') {if (!nesting--) break;}
|
||||
buffer[length++] = x;
|
||||
expand_buffer(length);
|
||||
}
|
||||
buffer[length] = 0;
|
||||
return buffer;
|
||||
|
||||
} else if (x == '{' || x == '}') {
|
||||
// all the punctuation is a word:
|
||||
buffer[0] = x;
|
||||
buffer[1] = 0;
|
||||
return buffer;
|
||||
|
||||
} else {
|
||||
|
||||
// read in an unquoted word:
|
||||
int length = 0;
|
||||
for (;;) {
|
||||
if (x == '\\') {x = read_quoted(); if (x<0) continue;}
|
||||
else if (x<0 || isspace(x & 255) || x=='{' || x=='}' || x=='#') break;
|
||||
buffer[length++] = x;
|
||||
expand_buffer(length);
|
||||
x = nextchar();
|
||||
}
|
||||
ungetc(x, fin);
|
||||
buffer[length] = 0;
|
||||
return buffer;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a word and interpret it as an integer value.
|
||||
\return integer value, or 0 if the word is not an integer
|
||||
*/
|
||||
int Fd_Project_Reader::read_int() {
|
||||
const char *word = read_word();
|
||||
if (word) {
|
||||
return atoi(word);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Read fdesign name/value pairs.
|
||||
Fdesign is the file format of the XForms UI designer. It stores lists of name
|
||||
and value pairs separated by a colon: `class: FL_LABELFRAME`.
|
||||
\param[out] name string
|
||||
\param[out] value string
|
||||
\return 0 if end of file, else 1
|
||||
*/
|
||||
int Fd_Project_Reader::read_fdesign_line(const char*& name, const char*& value) {
|
||||
int length = 0;
|
||||
int x;
|
||||
// find a colon:
|
||||
for (;;) {
|
||||
x = nextchar();
|
||||
if (x < 0 && feof(fin)) return 0;
|
||||
if (x == '\n') {length = 0; continue;} // no colon this line...
|
||||
if (!isspace(x & 255)) {
|
||||
buffer[length++] = x;
|
||||
expand_buffer(length);
|
||||
}
|
||||
if (x == ':') break;
|
||||
}
|
||||
int valueoffset = length;
|
||||
buffer[length-1] = 0;
|
||||
|
||||
// skip to start of value:
|
||||
for (;;) {
|
||||
x = nextchar();
|
||||
if ((x < 0 && feof(fin)) || x == '\n' || !isspace(x & 255)) break;
|
||||
}
|
||||
|
||||
// read the value:
|
||||
for (;;) {
|
||||
if (x == '\\') {x = read_quoted(); if (x<0) continue;}
|
||||
else if (x == '\n') break;
|
||||
buffer[length++] = x;
|
||||
expand_buffer(length);
|
||||
x = nextchar();
|
||||
}
|
||||
buffer[length] = 0;
|
||||
name = buffer;
|
||||
value = buffer+valueoffset;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Lookup table from fdesign .fd files to .fl files
|
||||
static const char *class_matcher[] = {
|
||||
"FL_CHECKBUTTON", "Fl_Check_Button",
|
||||
"FL_ROUNDBUTTON", "Fl_Round_Button",
|
||||
"FL_ROUND3DBUTTON", "Fl_Round_Button",
|
||||
"FL_LIGHTBUTTON", "Fl_Light_Button",
|
||||
"FL_FRAME", "Fl_Box",
|
||||
"FL_LABELFRAME", "Fl_Box",
|
||||
"FL_TEXT", "Fl_Box",
|
||||
"FL_VALSLIDER", "Fl_Value_Slider",
|
||||
"FL_MENU", "Fl_Menu_Button",
|
||||
"3", "FL_BITMAP",
|
||||
"1", "FL_BOX",
|
||||
"71","FL_BROWSER",
|
||||
"11","FL_BUTTON",
|
||||
"4", "FL_CHART",
|
||||
"42","FL_CHOICE",
|
||||
"61","FL_CLOCK",
|
||||
"25","FL_COUNTER",
|
||||
"22","FL_DIAL",
|
||||
"101","FL_FREE",
|
||||
"31","FL_INPUT",
|
||||
"12","Fl_Light_Button",
|
||||
"41","FL_MENU",
|
||||
"23","FL_POSITIONER",
|
||||
"13","Fl_Round_Button",
|
||||
"21","FL_SLIDER",
|
||||
"2", "FL_BOX", // was FL_TEXT
|
||||
"62","FL_TIMER",
|
||||
"24","Fl_Value_Slider",
|
||||
0};
|
||||
|
||||
|
||||
/**
|
||||
Finish a group of widgets and optionally transform its children's coordinates.
|
||||
|
||||
Implements the same functionality as Fl_Group::forms_end() from the forms
|
||||
compatibility library would have done:
|
||||
|
||||
- resize the group to surround its children if the group's w() == 0
|
||||
- optionally flip the \p y coordinates of all children relative to the group's window
|
||||
- Fl_Group::end() the group
|
||||
|
||||
\note Copied from forms_compatibility.cxx and modified as a static fluid
|
||||
function so we don't have to link to fltk_forms.
|
||||
|
||||
\param[in] g the Fl_Group widget
|
||||
\param[in] flip flip children's \p y coordinates if true (non-zero)
|
||||
*/
|
||||
static void forms_end(Fl_Group *g, int flip) {
|
||||
// set the dimensions of a group to surround its contents
|
||||
const int nc = g->children();
|
||||
if (nc && !g->w()) {
|
||||
Fl_Widget*const* a = g->array();
|
||||
Fl_Widget* o = *a++;
|
||||
int rx = o->x();
|
||||
int ry = o->y();
|
||||
int rw = rx+o->w();
|
||||
int rh = ry+o->h();
|
||||
for (int i = nc - 1; i--;) {
|
||||
o = *a++;
|
||||
if (o->x() < rx) rx = o->x();
|
||||
if (o->y() < ry) ry = o->y();
|
||||
if (o->x() + o->w() > rw) rw = o->x() + o->w();
|
||||
if (o->y() + o->h() > rh) rh = o->y() + o->h();
|
||||
}
|
||||
g->Fl_Widget::resize(rx, ry, rw-rx, rh-ry);
|
||||
}
|
||||
// flip all the children's coordinate systems:
|
||||
if (nc && flip) {
|
||||
Fl_Widget* o = (g->as_window()) ? g : g->window();
|
||||
int Y = o->h();
|
||||
Fl_Widget*const* a = g->array();
|
||||
for (int i = nc; i--;) {
|
||||
Fl_Widget* ow = *a++;
|
||||
int newy = Y - ow->y() - ow->h();
|
||||
ow->Fl_Widget::resize(ow->x(), newy, ow->w(), ow->h());
|
||||
}
|
||||
}
|
||||
g->end();
|
||||
}
|
||||
|
||||
/**
|
||||
Read a XForms design file.
|
||||
.fl and .fd file start with the same header. Fluid can recognize .fd XForms
|
||||
Design files by a magic number. It will read them and map XForms widgets onto
|
||||
FLTK widgets.
|
||||
\see http://xforms-toolkit.org
|
||||
*/
|
||||
void Fd_Project_Reader::read_fdesign() {
|
||||
int fdesign_magic = atoi(read_word());
|
||||
fdesign_flip = (fdesign_magic < 13000);
|
||||
Fl_Widget_Type *window = 0;
|
||||
Fl_Widget_Type *group = 0;
|
||||
Fl_Widget_Type *widget = 0;
|
||||
if (!Fl_Type::current) {
|
||||
Fl_Type *t = add_new_widget_from_file("Function", Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
t->name("create_the_forms()");
|
||||
Fl_Type::current = t;
|
||||
}
|
||||
for (;;) {
|
||||
const char *name;
|
||||
const char *value;
|
||||
if (!read_fdesign_line(name, value)) break;
|
||||
|
||||
if (!strcmp(name,"Name")) {
|
||||
|
||||
window = (Fl_Widget_Type*)add_new_widget_from_file("Fl_Window", Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
window->name(value);
|
||||
window->label(value);
|
||||
Fl_Type::current = widget = window;
|
||||
|
||||
} else if (!strcmp(name,"class")) {
|
||||
|
||||
if (!strcmp(value,"FL_BEGIN_GROUP")) {
|
||||
group = widget = (Fl_Widget_Type*)add_new_widget_from_file("Fl_Group", Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
Fl_Type::current = group;
|
||||
} else if (!strcmp(value,"FL_END_GROUP")) {
|
||||
if (group) {
|
||||
Fl_Group* g = (Fl_Group*)(group->o);
|
||||
g->begin();
|
||||
forms_end(g, fdesign_flip);
|
||||
Fl_Group::current(0);
|
||||
}
|
||||
group = widget = 0;
|
||||
Fl_Type::current = window;
|
||||
} else {
|
||||
for (int i = 0; class_matcher[i]; i += 2)
|
||||
if (!strcmp(value,class_matcher[i])) {
|
||||
value = class_matcher[i+1]; break;}
|
||||
widget = (Fl_Widget_Type*)add_new_widget_from_file(value, Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
if (!widget) {
|
||||
printf("class %s not found, using Fl_Button\n", value);
|
||||
widget = (Fl_Widget_Type*)add_new_widget_from_file("Fl_Button", Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (widget) {
|
||||
if (!widget->read_fdesign(name, value))
|
||||
printf("Ignoring \"%s: %s\"\n", name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Fd_Project_Writer ---------------------------------------------- MARK: -
|
||||
|
||||
/** \brief Construct local project writer. */
|
||||
Fd_Project_Writer::Fd_Project_Writer()
|
||||
: fout(NULL),
|
||||
needspace(0),
|
||||
write_codeview_(false)
|
||||
{
|
||||
}
|
||||
|
||||
/** \brief Release project writer resources. */
|
||||
Fd_Project_Writer::~Fd_Project_Writer()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
Open the .fl design file for writing.
|
||||
If the filename is NULL, associate stdout instead.
|
||||
\param[in] s the filename or NULL for stdout
|
||||
\return 1 if successful. 0 if the operation failed
|
||||
*/
|
||||
int Fd_Project_Writer::open_write(const char *s) {
|
||||
if (!s) {
|
||||
fout = stdout;
|
||||
} else {
|
||||
FILE *f = fl_fopen(s,"wb");
|
||||
if (!f) return 0;
|
||||
fout = f;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
Close the .fl design file.
|
||||
Don't close, if data was sent to stdout.
|
||||
\return 1 if succeeded, 0 if fclose failed
|
||||
*/
|
||||
int Fd_Project_Writer::close_write() {
|
||||
if (fout != stdout) {
|
||||
int x = fclose(fout);
|
||||
fout = stdout;
|
||||
return x >= 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** \brief Write an .fl design description file.
|
||||
\param[in] filename create this file, and if it exists, overwrite it
|
||||
\param[in] selected_only write only the selected nodes in the widget_tree. This
|
||||
is used to implement copy and paste.
|
||||
\param[in] sv if set, this file will be used by codeview
|
||||
\return 0 if the operation failed, 1 if it succeeded
|
||||
*/
|
||||
int Fd_Project_Writer::write_project(const char *filename, int selected_only, bool sv) {
|
||||
write_codeview_ = sv;
|
||||
undo_suspend();
|
||||
if (!open_write(filename)) {
|
||||
undo_resume();
|
||||
return 0;
|
||||
}
|
||||
write_string("# data file for the Fltk User Interface Designer (fluid)\n"
|
||||
"version %.4f",FL_VERSION);
|
||||
if(!g_project.include_H_from_C)
|
||||
write_string("\ndo_not_include_H_from_C");
|
||||
if(g_project.use_FL_COMMAND)
|
||||
write_string("\nuse_FL_COMMAND");
|
||||
if (g_project.utf8_in_src)
|
||||
write_string("\nutf8_in_src");
|
||||
if (g_project.avoid_early_includes)
|
||||
write_string("\navoid_early_includes");
|
||||
if (g_project.i18n_type) {
|
||||
write_string("\ni18n_type %d", g_project.i18n_type);
|
||||
switch (g_project.i18n_type) {
|
||||
case FD_I18N_NONE:
|
||||
break;
|
||||
case FD_I18N_GNU : /* GNU gettext */
|
||||
write_string("\ni18n_include"); write_word(g_project.i18n_gnu_include.c_str());
|
||||
write_string("\ni18n_conditional"); write_word(g_project.i18n_gnu_conditional.c_str());
|
||||
write_string("\ni18n_gnu_function"); write_word(g_project.i18n_gnu_function.c_str());
|
||||
write_string("\ni18n_gnu_static_function"); write_word(g_project.i18n_gnu_static_function.c_str());
|
||||
break;
|
||||
case FD_I18N_POSIX : /* POSIX catgets */
|
||||
write_string("\ni18n_include"); write_word(g_project.i18n_pos_include.c_str());
|
||||
write_string("\ni18n_conditional"); write_word(g_project.i18n_pos_conditional.c_str());
|
||||
if (!g_project.i18n_pos_file.empty()) {
|
||||
write_string("\ni18n_pos_file");
|
||||
write_word(g_project.i18n_pos_file.c_str());
|
||||
}
|
||||
write_string("\ni18n_pos_set"); write_word(g_project.i18n_pos_set.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected_only) {
|
||||
write_string("\nheader_name"); write_word(g_project.header_file_name.c_str());
|
||||
write_string("\ncode_name"); write_word(g_project.code_file_name.c_str());
|
||||
g_layout_list.write(this);
|
||||
if (g_shell_config)
|
||||
g_shell_config->write(this);
|
||||
if (g_project.write_mergeback_data)
|
||||
write_string("\nmergeback %d", g_project.write_mergeback_data);
|
||||
}
|
||||
|
||||
for (Fl_Type *p = Fl_Type::first; p;) {
|
||||
if (!selected_only || p->selected) {
|
||||
p->write(*this);
|
||||
write_string("\n");
|
||||
int q = p->level;
|
||||
for (p = p->next; p && p->level > q; p = p->next) {/*empty*/}
|
||||
} else {
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
int ret = close_write();
|
||||
undo_resume();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
Write a string to the .fl file, quoting characters if necessary.
|
||||
\param[in] w NUL terminated text
|
||||
*/
|
||||
void Fd_Project_Writer::write_word(const char *w) {
|
||||
if (needspace) putc(' ', fout);
|
||||
needspace = 1;
|
||||
if (!w || !*w) {fprintf(fout,"{}"); return;}
|
||||
const char *p;
|
||||
// see if it is a single word:
|
||||
for (p = w; is_id(*p); p++) ;
|
||||
if (!*p) {fprintf(fout,"%s",w); return;}
|
||||
// see if there are matching braces:
|
||||
int n = 0;
|
||||
for (p = w; *p; p++) {
|
||||
if (*p == '{') n++;
|
||||
else if (*p == '}') {n--; if (n<0) break;}
|
||||
}
|
||||
int mismatched = (n != 0);
|
||||
// write out brace-quoted string:
|
||||
putc('{', fout);
|
||||
for (; *w; w++) {
|
||||
switch (*w) {
|
||||
case '{':
|
||||
case '}':
|
||||
if (!mismatched) break;
|
||||
case '\\':
|
||||
case '#':
|
||||
putc('\\',fout);
|
||||
break;
|
||||
}
|
||||
putc(*w,fout);
|
||||
}
|
||||
putc('}', fout);
|
||||
}
|
||||
|
||||
/**
|
||||
Write an arbitrary formatted word to the .fl file, or a comment, etc .
|
||||
If needspace is set, then one space is written before the string
|
||||
unless the format starts with a newline character \\n.
|
||||
\param[in] format printf style formatting string followed by a list of arguments
|
||||
*/
|
||||
void Fd_Project_Writer::write_string(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (needspace && *format != '\n') fputc(' ',fout);
|
||||
vfprintf(fout, format, args);
|
||||
va_end(args);
|
||||
needspace = !isspace(format[strlen(format)-1] & 255);
|
||||
}
|
||||
|
||||
/**
|
||||
Start a new line in the .fl file and indent it for a given nesting level.
|
||||
\param[in] n indent level
|
||||
*/
|
||||
void Fd_Project_Writer::write_indent(int n) {
|
||||
fputc('\n',fout);
|
||||
while (n--) {fputc(' ',fout); fputc(' ',fout);}
|
||||
needspace = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Write a '{' to the .fl file at the given indenting level.
|
||||
*/
|
||||
void Fd_Project_Writer::write_open() {
|
||||
if (needspace) fputc(' ',fout);
|
||||
fputc('{',fout);
|
||||
needspace = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Write a '}' to the .fl file at the given indenting level.
|
||||
\param[in] n indent level
|
||||
*/
|
||||
void Fd_Project_Writer::write_close(int n) {
|
||||
if (needspace) write_indent(n);
|
||||
fputc('}',fout);
|
||||
needspace = 1;
|
||||
}
|
||||
|
||||
/// \}
|
||||
94
fluid/io/Project_Writer.h
Normal file
94
fluid/io/Project_Writer.h
Normal file
@ -0,0 +1,94 @@
|
||||
//
|
||||
// Fluid file routines for the Fast Light Tool Kit (FLTK).
|
||||
//
|
||||
// Copyright 1998-2023 by Bill Spitzak and others.
|
||||
//
|
||||
// This library is free software. Distribution and use rights are outlined in
|
||||
// the file "COPYING" which should have been included with this file. If this
|
||||
// file is missing or damaged, see the license at:
|
||||
//
|
||||
// https://www.fltk.org/COPYING.php
|
||||
//
|
||||
// Please see the following page on how to report bugs and issues:
|
||||
//
|
||||
// https://www.fltk.org/bugs.php
|
||||
//
|
||||
|
||||
#ifndef _FLUID_FILE_H
|
||||
#define _FLUID_FILE_H
|
||||
|
||||
#include "nodes/Fl_Type.h"
|
||||
|
||||
#include <FL/fl_attr.h>
|
||||
|
||||
class Fl_Type;
|
||||
|
||||
extern int fdesign_flip;
|
||||
|
||||
int read_file(const char *, int merge, Strategy strategy=Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
int write_file(const char *, int selected_only = 0, bool to_codeview = false);
|
||||
|
||||
class Fd_Project_Reader
|
||||
{
|
||||
protected:
|
||||
/// Project input file
|
||||
FILE *fin;
|
||||
/// Number of most recently read line
|
||||
int lineno;
|
||||
/// Pointer to the file path and name (not copied!)
|
||||
const char *fname;
|
||||
/// Expanding buffer to store the most recently read word
|
||||
char *buffer;
|
||||
/// Exact size of the expanding buffer in bytes
|
||||
int buflen;
|
||||
|
||||
void expand_buffer(int length);
|
||||
|
||||
int nextchar() { for (;;) { int ret = fgetc(fin); if (ret!='\r') return ret; } }
|
||||
|
||||
public:
|
||||
/// Holds the file version number after reading the "version" tag
|
||||
double read_version;
|
||||
|
||||
public:
|
||||
Fd_Project_Reader();
|
||||
~Fd_Project_Reader();
|
||||
int open_read(const char *s);
|
||||
int close_read();
|
||||
const char *filename_name();
|
||||
int read_quoted();
|
||||
Fl_Type *read_children(Fl_Type *p, int merge, Strategy strategy, char skip_options=0);
|
||||
int read_project(const char *, int merge, Strategy strategy=Strategy::FROM_FILE_AS_LAST_CHILD);
|
||||
void read_error(const char *format, ...);
|
||||
const char *read_word(int wantbrace = 0);
|
||||
int read_int();
|
||||
int read_fdesign_line(const char*& name, const char*& value);
|
||||
void read_fdesign();
|
||||
};
|
||||
|
||||
class Fd_Project_Writer
|
||||
{
|
||||
protected:
|
||||
// Project output file, always opened in "wb" mode
|
||||
FILE *fout;
|
||||
/// If set, one space is written before text unless the format starts with a newline character
|
||||
int needspace;
|
||||
/// Set if this file will be used in the codeview dialog
|
||||
bool write_codeview_;
|
||||
|
||||
public:
|
||||
Fd_Project_Writer();
|
||||
~Fd_Project_Writer();
|
||||
int open_write(const char *s);
|
||||
int close_write();
|
||||
int write_project(const char *filename, int selected_only, bool codeview);
|
||||
void write_word(const char *);
|
||||
void write_string(const char *,...) __fl_attr((__format__ (__printf__, 2, 3)));
|
||||
void write_indent(int n);
|
||||
void write_open();
|
||||
void write_close(int n);
|
||||
FILE *file() const { return fout; }
|
||||
bool write_codeview() const { return write_codeview_; }
|
||||
};
|
||||
|
||||
#endif // _FLUID_FILE_H
|
||||
Loading…
Reference in New Issue
Block a user