// // fltk-options for the Fast Light Tool Kit (FLTK). // // Copyright 2022-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 #include #include #include #include #include #include #include #include #include #include #include #include "../src/flstring.h" #include #include // user interface sizes // |<->|<- group browser ->|<->|<- options ->|<->| const int FO_GAP = 10; const int FO_BROWSER_W = 200; const int FO_SCROLL_W = 16 + 4; //Fl::scrollbar_size() + Fl::box_dw(FL_DOWN_BOX); const int FO_CHOICE_W = 75; const int FO_OPTIONS_W = 420; const int FO_BUTTON_W = 75; const int FO_WINDOW_W = FO_GAP + FO_BROWSER_W + FO_GAP + FO_SCROLL_W + FO_OPTIONS_W + FO_SCROLL_W + FO_GAP; const int FO_SYSTEM_X = FO_OPTIONS_W - 2*FO_GAP - 2*FO_CHOICE_W; const int FO_USER_X = FO_OPTIONS_W - FO_GAP - FO_CHOICE_W; const int FO_TITLE_H = 20; const int FO_BROWSER_H = 340; const int FO_BUTTON_H = 25; const int FO_CHOICE_H = 22; const int FO_WINDOW_H = FO_GAP + FO_BROWSER_H + FO_GAP + FO_BUTTON_H + FO_GAP; Fl_Window* g_window = NULL; Fl_Hold_Browser* g_headline_browser = NULL; Fl_Scroll* g_option_scroll = NULL; // command line options int g_verbose = 0; int g_batch_mode = 0; int g_system_write_ok = 0; int g_user_write_ok = 0; // Magic value when calling the handle function. const int FO_PRINT_VALUE = -999; // User or system setting typedef enum { FO_SYSTEM, FO_USER } Fo_Context; // Avalable option types. // We can add more types later as needed. // See: Fl_Group* add_option( Fo_Option_Descr* opt) // See: Fl_Group* add_option_bool(Fo_Option_Descr* opt) typedef enum { FO_END_OF_LIST = 0, FO_HEADLINE, FO_OPTION_BOOL // FO_OPTION_INT, // not yet implemented // FO_OPTION_TEXT, // not yet implemented // FO_OPTION_SCHEME, // not yet implemented } Fo_Type; // Record holding the information for headlines and options. typedef struct { Fo_Type type; const char* text; Fl::Fl_Option id; const char* name; const char* prefs_name; bool bool_default; const char* brief; const char* tooltip; Fl_Group* ui; } Fo_Option_Descr; // List of all entries in the user interface, including headlines and options. // This list can be arbitrarily extended. The app will generate a scrollable // area if the list does not fit the app window. Fo_Option_Descr g_option_list[] = { { FO_HEADLINE, "Keyboard Focus Options" }, { FO_OPTION_BOOL, "Visible Keyboard Focus:", Fl::OPTION_VISIBLE_FOCUS, "OPTION_VISIBLE_FOCUS", "VisibleFocus", true, "Draw a dotted rectangle in widget with keyboard focus.", "If visible focus is switched on, FLTK will draw a dotted rectangle inside " "the widget that will receive the next keystroke. If switched off, no such " "indicator will be drawn and keyboard navigation is disabled." }, { FO_OPTION_BOOL, "Arrow Keys move Focus:", Fl::OPTION_ARROW_FOCUS, "OPTION_ARROW_FOCUS", "ArrowFocus", false, "Arrow keys will move focus beyond text input field.", "When switched on, moving the text cursor beyond the start or end of the " "text in a text widget will change focus to the next widget. When switched " "off, the cursor will stop at the end of the text. Pressing Tab or Ctrl-Tab " "will advance the keyboard focus. Switch this on, if you want the old " "behavior of FLTK 1.1." }, { FO_HEADLINE, "Tooltip Options" }, { FO_OPTION_BOOL, "Show Tooltips:", Fl::OPTION_SHOW_TOOLTIPS, "OPTION_SHOW_TOOLTIPS", "ShowTooltips", true, "Show or hide tooltips.", "If tooltips are enabled, hovering the mouse over a widget with a tooltip " "text will open a little tooltip window until the mouse leaves the widget. " "If disabled, no tooltip is shown." }, { FO_HEADLINE, "Drag And Drop Options" }, { FO_OPTION_BOOL, "Allow dragging Text:", Fl::OPTION_DND_TEXT, "OPTION_DND_TEXT", "DNDText", true, "User can drag text from FLTK into other apps.", "If text drag-and-drop is enabled, the user can select and drag text from " "any text widget. If disabled, no dragging is possible, however dropping " "text from other applications still works." }, { FO_HEADLINE, "Native File Chooser Options" }, { FO_OPTION_BOOL, "Native File Chooser uses GTK:", Fl::OPTION_FNFC_USES_GTK, "OPTION_FNFC_USES_GTK", "FNFCUsesGTK", true, "Use GTK file chooser instead of FLTK if available.", "If 'Native File Chooser uses GTK' is enabled, the Fl_Native_File_Chooser " "class calls the GTK open/save file dialogs when they are available on the " "platfom. If disabled, the Fl_Native_File_Chooser class always uses FLTK's " "own file dialog (i.e., Fl_File_Chooser) even if GTK is available." }, { FO_OPTION_BOOL, "Native File Chooser uses Zenity:", Fl::OPTION_FNFC_USES_ZENITY, "OPTION_FNFC_USES_ZENITY", "UseZenity", false, "Fl_Native_File_Chooser uses the 'zenity' command if possible.", "Meaningful for the Wayland/X11 platform only. When switched on, " "the library uses a Zenity-based file dialog if command 'zenity' is available. " "When switched off (default), command 'zenity' is not used."}, { FO_OPTION_BOOL, "Native File Chooser uses Kdialog:", Fl::OPTION_FNFC_USES_KDIALOG, "OPTION_FNFC_USES_KDIALOG", "UseKdialog", false, "Fl_Native_File_Chooser uses the 'kdialog' command if possible.", "Meaningful for the Wayland/X11 platform. " "When switched on, the library uses a kdialog-based file dialog if command 'kdialog' is " "available. When switched off (default), command 'kdialog' is not used." }, { FO_HEADLINE, "Print dialog Options" }, { FO_OPTION_BOOL, "Print dialog uses GTK:", Fl::OPTION_PRINTER_USES_GTK, "OPTION_PRINTER_USES_GTK", "PrintUsesGTK", true, "Use GTK printer dialog instead of FLTK if available.", "If 'Print dialog uses GTK' is enabled, the Fl_Printer class calls the " "GTK print dialog when it's available on the platfom. If disabled, the " "Fl_Printer class always uses FLTK's own print dialog even " "if GTK is available." }, { FO_HEADLINE, "Scaling Factor Options" }, { FO_OPTION_BOOL, "Transiently show scaling factor:", Fl::OPTION_SHOW_SCALING, "OPTION_SHOW_SCALING", "ShowZoomFactor", true, "Show the zoom factor in a transient popup window.", "If 'Transiently show scaling factor' is enabled, the library shows in a " "transient popup window the display scaling factor value when it is " "changed. If disabled, no such transient window is used." }, { FO_OPTION_BOOL, "Allow simple zoom-in shortcut:", Fl::OPTION_SIMPLE_ZOOM_SHORTCUT, "OPTION_SIMPLE_ZOOM_SHORTCUT", "SimpleZoomShortcut", false, "Fine tune the shortcut that triggers the zoom-in operation.", "When the keyboard in use has '+' in the shifted position of its key, " "pressing that key and ctrl triggers the zoom-in operation. " "If disabled, the zoom-in operation requires the shift key to be pressed also " "with such a keyboard." }, // -- When adding new options here, please make sure that you also update // -- documentation.src/fltk-options.dox // -- and // -- documentation.src/fltk-options.man { FO_END_OF_LIST } }; /** Check for write permission. \param[in] ctx settings context \return true if writing to the requested preferences is ok */ bool write_permission(Fo_Context ctx) { if (ctx == FO_SYSTEM) return g_system_write_ok; else return g_user_write_ok; } /** Write an option into the system or user preferences file. \param[in] ctx settings context \param[in] name the name of the option \param[in] value 0 or 1, or -1 to remove the entry, so it will be reset to its default value */ void set_option(Fo_Context ctx, const char *name, int value) { enum Fl_Preferences::Root context = (ctx==FO_SYSTEM) ? Fl_Preferences::CORE_SYSTEM : Fl_Preferences::CORE_USER; Fl_Preferences prefs(context, "fltk.org", "fltk"); Fl_Preferences options(prefs, "options"); if (value==-1) options.deleteEntry(name); else options.set(name, value); } /** Read an option from the system or user preferences file. \param[in] ctx settings context \param[in] name the name of the option \return 0 or 1, or -1 if the value is not set */ int get_option(Fo_Context ctx, const char *name) { int value = -1; enum Fl_Preferences::Root context = (ctx==FO_SYSTEM) ? Fl_Preferences::SYSTEM_L : Fl_Preferences::USER_L; Fl_Preferences prefs(context, "fltk.org", "fltk"); Fl_Preferences options(prefs, "options"); options.get(name, value, -1); return value; } /** Delete an option in the system or user preferences file. \param[in] ctx settings context \param[in] name the name of the option */ void clear_option(Fo_Context ctx, const char *name) { set_option(ctx, name, -1); } /** Print the Usage: text and list all system and user options. \param[in] argv application name when called from the shell */ void print_usage(const char *argv0) { const char *app_name = NULL; if (argv0 && argv0[0]) app_name = fl_filename_name(argv0); if (!app_name || !app_name[0]) app_name = "fltk-options"; fprintf(stdout, "FLTK %d.%d.%d. Usage:\n", FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION); fprintf(stdout, "%s [-Soption[=val]] [-Uoption[=val]] [-L] [-LS] [-LU] [-f] [-v] [-h[option]]\n", app_name); fprintf(stdout, " -Soption[=value] change or print system wide option\n"); fprintf(stdout, " -Uoption[=value] change or print user option\n"); fprintf(stdout, " Values can be 0 or OFF to clear, or 1 or ON to set the option.\n" " The value -1 or DEFAULT sets the option to its default value.\n" " If no value is given, the current setting is returned as -1, 0, or 1.\n"); fprintf(stdout, " -L, -LS, -LU list the value of all options, of all system settings, \n" " or of all user setting\n"); fprintf(stdout, " -f suppresses error messages concerning file access permissions\n"); fprintf(stdout, " -v, --verbose prints additional information in command line mode\n"); fprintf(stdout, " -h[option], --help [option] general help, or info for the given option\n\n"); fprintf(stdout, " This version of %s supports the following options:\n", app_name); Fo_Option_Descr *opt; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if (opt->name) { if (opt->brief) fprintf(stdout, " %-24s %s\n", opt->name, opt->brief); else fprintf(stdout, " %s\n", opt->name); } } fprintf(stdout, "\n Calling %s without options will launch %s interactive mode.\n", app_name, app_name); } /** Print more information for a given options. \param[in] options the name of the option, case insensitive */ void print_info(const char *option) { Fo_Option_Descr *opt; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if ( opt->name && (fl_ascii_strcasecmp(opt->name, option) == 0) ) { if (opt->brief) fprintf(stdout, "%s: %s\n", opt->name, opt->brief); else fprintf(stdout, "%s: see FLTK manual for details\n", opt->name); if (opt->tooltip) fprintf(stdout, "\n%s\n", opt->tooltip); fprintf(stdout, "\nDefault is %s.\n", opt->bool_default ? "on" : "off"); break; } } if (opt->type == FO_END_OF_LIST) fprintf(stderr, "Warning: Unrecognized option \"%s\".\n", option); } /** List the current value of all options. \param[in] cmd 'S' lists system options, 'U' lists user options, 0 lists both */ void list_options(char cmd) { Fo_Option_Descr *opt; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if (opt->name) { printf("%-24s", opt->name); if (cmd == 'S' || cmd == 0) { int value = get_option(FO_SYSTEM, opt->prefs_name); printf(" system:%2d", value); } if (cmd == 0) printf(","); if (cmd == 'U' || cmd == 0) { int value = get_option(FO_USER, opt->prefs_name); printf(" user:%2d", value); } printf("\n"); } } } /** Handle a commmand line argument for system or user options. \param[in] ctx settings context \param[in] name the name of the option \param ival 0 or 1 to set, -1 to reset to default, and FO_PRINT_VALUE to print the current value */ void handle_option(Fo_Context ctx, const char *name, int ival) { const char *ctx_name = (ctx==FO_SYSTEM) ? "system" : "user"; Fo_Option_Descr *opt; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if ( opt->name && (fl_ascii_strcasecmp(opt->name, name) == 0) ) { if (ival == FO_PRINT_VALUE) { int value = get_option(ctx, opt->prefs_name); if (g_verbose) printf("Current value for %s option %s is %d\n", ctx_name, name, value); else printf("%d\n", value); } else if (ival ==-1) { if (g_verbose) printf("Reset %s option %s to default\n", ctx_name, name); clear_option(ctx, opt->prefs_name); } else { if (g_verbose) printf("Set %s option %s to %d\n", ctx_name, name, ival); set_option(ctx, opt->prefs_name, ival); } if ( (ival != FO_PRINT_VALUE) && !write_permission(ctx) ) { fprintf(stderr, "ERROR: No write permission for %s options\n", ctx_name); exit(1); } break; } } if (opt->type == FO_END_OF_LIST) fprintf(stderr, "Warning: Unrecognized %s option \"%s\".\n", ctx_name, name); } /** FLTK callback for every item in the command line. Command line arguments start with "-S" for system wide settings or "-U" for user settings, followed by the option name, followed by a value "=0", "=1", "=ON", or "=OFF". If no value is give, we will print a "0" or "1" to stdout. \param[in] argc number of arguments in the list \param[in] argv pointer to the list of arguments \param[inout] index into current argument, increment for every used entry \return 1 if the entry was correct, 0 if it was not understood */ static int read_command_line_args(int argc, char** argv, int& i) { char cmd = 0; char opt[64] = ""; char val[32] = ""; int ival = FO_PRINT_VALUE; const char *arg = argv[i++]; if ( (strcmp(arg, "--help") == 0)) { if (argv[i] && argv[i][0]) { print_info(argv[i]); i++; } else { print_usage(argv[0]); } g_batch_mode = 1; return 1; } if ( (strncmp(arg, "-h", 2) == 0) ) { if (arg[2]) { print_info(arg+2); i++; } else { print_usage(argv[0]); } g_batch_mode = 1; return 1; } if ( (strcmp(arg, "--help") == 0) || (strcmp(arg, "-h") == 0) ) { print_usage(argv[0]); g_batch_mode = 1; return 1; } if ( (strcmp(arg, "--verbose") == 0) || (strcmp(arg, "-v") == 0) ) { g_verbose = 1; return 1; } if (strcmp(arg, "-f") == 0) { // suppress write access error g_system_write_ok = g_user_write_ok = 1; return 1; } if (arg[0] == '-') { cmd = arg[1]; if (cmd == 'U' || cmd == 'S') { const char *eq = strchr(arg+2, '='); if (eq) { size_t n = (eq - (arg+2)); if (n==0) { i--; return 0; } if (n > sizeof(opt)-1) n = sizeof(opt)-1; strlcpy(opt, arg+2, n+1); strlcpy(val, eq+1, sizeof(val)); if (fl_ascii_strcasecmp(val, "ON")==0) ival = 1; else if (fl_ascii_strcasecmp(val, "OFF")==0) ival = 0; else if (fl_ascii_strcasecmp(val, "DEFAULT")==0) ival = -1; else if (strcmp(val, "1")==0) ival = 1; else if (strcmp(val, "0")==0) ival = 0; else if (strcmp(val, "-1")==0) ival = -1; else { fprintf(stderr, "Warning: Unrecognized value \"%s\" for option \"%s\".\n", val, opt); g_batch_mode = 1; return 1; } } else { strlcpy(opt, arg+2, sizeof(opt)); } } } if (cmd == 'S') { // system setting handle_option(FO_SYSTEM, opt, ival); g_batch_mode = 1; return 1; } if (cmd == 'U') { // user setting handle_option(FO_USER, opt, ival); g_batch_mode = 1; return 1; } // check for -L, -LS, or -LU if (strcmp(arg, "-L")==0 || strcmp(arg, "-LS")==0 || strcmp(arg, "-LU")==0) { list_options(arg[2]); g_batch_mode = 1; return 1; } i--; return 0; } /** Check if we have write permission for either database. If there is no write permission for a set of options, the UI will show them grayed out. \param[out] sys system preferences write permission \param[out] user user preferences write permission */ void check_write_permissions(int &sys, int &user) { char path[FL_PATH_MAX+1]; sys = 0; Fl_Preferences sys_prefs(Fl_Preferences::SYSTEM_L, "fltk.org", "fltk"); if (sys_prefs.file_access() & Fl_Preferences::SYSTEM_WRITE_OK) { path[0] = 0; sys_prefs.filename(path, FL_PATH_MAX); if ( path[0] && (fl_access(path, 2) == 0) ) // W_OK sys = 1; } user = 0; Fl_Preferences user_prefs(Fl_Preferences::USER_L, "fltk.org", "fltk"); if (user_prefs.file_access() & Fl_Preferences::USER_WRITE_OK) { path[0] = 0; user_prefs.filename(path, FL_PATH_MAX); if ( path[0] && (fl_access(path, 2) == 0) ) // W_OK user = 1; } } /** Called when the UI Close button is clicked. */ static void close_cb(Fl_Widget*, void*) { Fl::hide_all_windows(); } /** Called when a boolenan system option is changed. */ void set_system_option_cb(Fl_Widget* w, void* user_data) { Fl_Choice* choice = (Fl_Choice*)w; Fo_Option_Descr* opt = (Fo_Option_Descr*)user_data; const Fl_Menu_Item* mi = choice->mvalue(); if (!mi) return; int value = (int)mi->argument(); set_option(FO_SYSTEM, opt->prefs_name, value); } /** Called when a boolenan user option is changed. */ void set_user_option_cb(Fl_Widget* w, void* user_data) { Fl_Choice* choice = (Fl_Choice*)w; Fo_Option_Descr* opt = (Fo_Option_Descr*)user_data; const Fl_Menu_Item* mi = choice->mvalue(); if (!mi) return; int value = (int)mi->argument(); set_option(FO_USER, opt->prefs_name, value); } /** Add an option group to the pack group. */ void add_option(Fl_Pack* pack, Fo_Option_Descr* opt) { static Fl_Menu_Item bool_option_menu[] = { { "off", 0, 0, (void*)(0), 0}, { "on", 0, 0, (void*)(1), FL_MENU_DIVIDER}, { "default", 0, 0, (void*)(-1), 0}, { NULL } }; // -- get the height of the tooltip text, so we can create the correct group size int tooltip_h = 0; if (opt->tooltip) { int ww = FO_OPTIONS_W - 2*FO_GAP - 6; // 6 is the FL_ALIGN_LEFT margin int hh = 100; fl_font(FL_HELVETICA, 11); fl_measure(opt->tooltip, ww, hh); tooltip_h = hh+5; } // -- create a group that contains all the UI elements for the option int yy = 0; Fl_Group* option_group = new Fl_Group(0, 0, FO_OPTIONS_W, 2*FO_GAP + FO_BUTTON_H + tooltip_h + 2*FO_GAP + FO_CHOICE_H +17); opt->ui = option_group; yy += FO_GAP; // -- name of the options Fl_Box* name = new Fl_Box(0, yy, FO_OPTIONS_W, 21); name->copy_label(opt->name); name->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE); name->labelfont(FL_HELVETICA_BOLD); yy += name->h(); // -- description Fl_Box* brief = new Fl_Box(FO_GAP, yy, FO_OPTIONS_W - 2*FO_GAP, tooltip_h, opt->brief); brief->labelsize(11); brief->labelcolor(fl_lighter(FL_FOREGROUND_COLOR)); brief->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); yy += 17; // -- tooltip, if one exists Fl_Box* tooltip = new Fl_Box(FO_GAP, yy, FO_OPTIONS_W - 2*FO_GAP, tooltip_h, opt->tooltip); tooltip->labelfont(FL_HELVETICA); tooltip->labelsize(11); tooltip->labelcolor(fl_lighter(FL_FOREGROUND_COLOR)); tooltip->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); yy += tooltip_h; // -- default setting Fl_Box* default_setting = new Fl_Box(FO_GAP, yy, FO_SYSTEM_X - FO_GAP, 14); char buf[64]; fl_snprintf(buf, 64, "Default is %s.",opt->bool_default ? "on" : "off"); // -- label the pulldown menus default_setting->copy_label(buf); default_setting->labelsize(11); default_setting->labelcolor(fl_lighter(FL_FOREGROUND_COLOR)); default_setting->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE); yy += 18; // -- option text Fl_Box* text = new Fl_Box(0, yy, FO_SYSTEM_X, FO_CHOICE_H); text->copy_label(opt->text); text->align(FL_ALIGN_RIGHT|FL_ALIGN_INSIDE); // -- system choice Fl_Choice* system_choice = new Fl_Choice(FO_SYSTEM_X, yy, FO_CHOICE_W, FO_CHOICE_H, "System:"); system_choice->down_box(FL_BORDER_BOX); system_choice->labelsize(11); system_choice->align(FL_ALIGN_TOP); system_choice->callback((Fl_Callback*)set_system_option_cb, (void*)opt); system_choice->menu(bool_option_menu); switch (get_option(FO_SYSTEM, opt->prefs_name)) { case 0: system_choice->value(0); break; case 1: system_choice->value(1); break; default: system_choice->value(2); break; } if (!g_system_write_ok) system_choice->deactivate(); // -- user choice Fl_Choice* user_choice = new Fl_Choice(FO_USER_X, yy, FO_CHOICE_W, FO_CHOICE_H, "User:"); user_choice->down_box(FL_BORDER_BOX); user_choice->labelsize(11); user_choice->align(FL_ALIGN_TOP); user_choice->callback((Fl_Callback*)set_user_option_cb, (void*)opt); user_choice->menu(bool_option_menu); switch (get_option(FO_USER, opt->prefs_name)) { case 0: user_choice->value(0); break; case 1: user_choice->value(1); break; default: user_choice->value(2); break; } if (!g_user_write_ok) user_choice->deactivate(); option_group->end(); } /** Fill the options list and make the unused options invisible. */ void add_options(Fl_Pack* pack) { Fo_Option_Descr *opt; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if (opt->type != FO_HEADLINE) add_option(pack, opt); } } void select_headline_cb(Fl_Widget*, void*) { int line = g_headline_browser->value(); if (line) { Fo_Option_Descr* sel = (Fo_Option_Descr*)g_headline_browser->data(line); Fo_Option_Descr *opt; bool show = false; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if (opt->type == FO_HEADLINE) show = false; if (opt == sel) { show = true; g_option_scroll->label(opt->text); } if (opt->ui) { if (show) opt->ui->show(); else opt->ui->hide(); } } } else { Fo_Option_Descr *opt; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if (opt->ui) opt->ui->show(); } g_option_scroll->label("All Options"); } g_option_scroll->scroll_to(-FO_SCROLL_W+Fl::box_dy(g_option_scroll->box()), 0); g_window->redraw(); } /** Fill the headline browser and set callbacks. */ void add_headlines(Fl_Hold_Browser* browser) { Fo_Option_Descr *opt; for (opt = g_option_list; opt->type!=FO_END_OF_LIST; ++opt) { if (opt->type == FO_HEADLINE) browser->add(opt->text, (void*)opt); } browser->callback(select_headline_cb, NULL); } /** Build the modular user interface (ALT VERSION). */ Fl_Window* build_ui() { Fl_Tooltip::size(11); // -- the main application window g_window = new Fl_Double_Window(FO_WINDOW_W, FO_WINDOW_H, "FLTK Options"); // -- a clickable browser for all headlines g_headline_browser = new Fl_Hold_Browser(FO_GAP, FO_GAP + FO_TITLE_H, FO_BROWSER_W, FO_BROWSER_H - FO_TITLE_H, "Groups"); g_headline_browser->align(FL_ALIGN_TOP); g_headline_browser->textsize(FL_NORMAL_SIZE+1); g_headline_browser->linespacing(4); add_headlines(g_headline_browser); // -- scrollable area for all options inside a group g_option_scroll = new Fl_Scroll(FO_GAP + FO_BROWSER_W + FO_GAP, FO_GAP + FO_TITLE_H, FO_OPTIONS_W +2*FO_SCROLL_W, FO_BROWSER_H - FO_TITLE_H, "All Options"); g_option_scroll->box(FL_DOWN_BOX); g_option_scroll->type(Fl_Scroll::VERTICAL); Fl_Pack* option_list = new Fl_Pack(g_option_scroll->x() + FO_SCROLL_W, g_option_scroll->y() + Fl::box_dy(FL_DOWN_BOX), FO_OPTIONS_W, 10); add_options(option_list); // -- end all groups g_option_scroll->end(); g_window->resizable(g_option_scroll); // -- close button Fl_Button* close = new Fl_Button(FO_WINDOW_W - 2*FO_GAP-FO_BUTTON_W, FO_WINDOW_H - FO_GAP - FO_BUTTON_H, FO_BUTTON_W, FO_BUTTON_H, "Close"); close->callback(close_cb); g_window->end(); g_window->size_range(FO_WINDOW_W, 200, FO_WINDOW_W, 32767); return g_window; } /** This is where it all begins. */ int main(int argc,char **argv) { check_write_permissions(g_system_write_ok, g_user_write_ok); int i = 1; Fl::args_to_utf8(argc, argv); // for MSYS2/MinGW int args_processed = Fl::args(argc, argv, i, read_command_line_args); if (args_processed < argc) { fprintf(stderr, "ERROR: Unrecognized command line option \"%s\".\n", argv[i]); return 1; } if (g_batch_mode) return 0; Fl_Window *win = build_ui(); win->show(argc, argv); Fl::run(); return 0; }