diff --git a/gui/m_addons.cc b/gui/m_addons.cc index c7d1d5f543..a2d8a7f378 100644 --- a/gui/m_addons.cc +++ b/gui/m_addons.cc @@ -1,687 +1,690 @@ -//---------------------------------------------------------------------- -// Addons Loading and Selection GUI -//---------------------------------------------------------------------- -// -// Oblige Level Maker -// -// Copyright (C) 2006-2016 Andrew Apted -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -//---------------------------------------------------------------------- - -#include "headers.h" -#include "hdr_fltk.h" -#include "hdr_ui.h" - -#include "physfs.h" - -#include "lib_argv.h" -#include "lib_file.h" -#include "lib_util.h" - -#include "main.h" -#include "m_addons.h" -#include "m_cookie.h" - - -// need this because the OPTIONS file is loaded *before* the addons -// folder is scanned for PK3 packages, so remember enabled ones here. -static std::map initial_enabled_addons; - -typedef struct -{ - const char *name; // base filename, includes ".pk3" extension - - bool enabled; - -} addon_info_t; - -static std::vector all_addons; - - -void VFS_AddFolder(const char *name) -{ - char *path = StringPrintf("%s/%s", install_dir, name); - char *mount = StringPrintf("/%s", name); - - if (! PHYSFS_mount(path, mount, 0)) - { - Main_FatalError("Failed to mount '%s' folder in PhysFS:\n%s\n", - name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return; /* NOT REACHED */ - } - - DebugPrintf("mounted folder '%s'\n", name); -} - - -bool VFS_AddArchive(const char *filename, bool options_file) -{ - LogPrintf(" using: %s\n", filename); - - if (! HasExtension(filename)) - filename = ReplaceExtension(filename, "pk3"); - else - filename = StringDup(filename); - - // when handling "bare" filenames from the command line (i.e. ones - // containing no paths or drive spec) and the file does not exist in - // the current dir, look for it in the standard addons/ folder. - if (options_file || - (! FileExists(filename) && - filename == fl_filename_name(filename))) - { - char *new_name = StringPrintf("%s/addons/%s", install_dir, filename); - StringFree(filename); - filename = new_name; - } - - if (! PHYSFS_mount(filename, "/", 0)) - { - if (options_file) - LogPrintf("Failed to mount '%s' archive in PhysFS:\n%s\n", - filename, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - else - Main_FatalError("Failed to mount '%s' archive in PhysFS:\n%s\n", - filename, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - - return false; - } - - return true; // Ok -} - - -void VFS_InitAddons(const char *argv0) -{ - LogPrintf("Initializing VFS...\n"); - - if (! PHYSFS_init(argv0)) - { - Main_FatalError("Failed to init PhysFS:\n%s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return; /* NOT REACHED */ - } - - VFS_AddFolder("scripts"); - VFS_AddFolder("games"); - VFS_AddFolder("engines"); - VFS_AddFolder("modules"); - VFS_AddFolder("data"); - - LogPrintf("DONE.\n\n"); -} - - -void VFS_ParseCommandLine() -{ - int arg = ArgvFind('a', "addon"); - int count = 0; - - if (arg < 0) - return; - - arg++; - - LogPrintf("Command-line addons....\n"); - - for (; arg < arg_count && ! ArgvIsOption(arg) ; arg++, count++) - { - VFS_AddArchive(arg_list[arg], false /* options_file */); - } - - if (! count) - Main_FatalError("Missing filename for --addon option\n"); - - LogPrintf("DONE\n\n"); -} - - -void VFS_OptParse(const char *name) -{ - // just remember it now - if (initial_enabled_addons.find(name) == initial_enabled_addons.end()) - { - initial_enabled_addons[name] = 1; - } -} - - -void VFS_OptWrite(FILE *fp) -{ - fprintf(fp, "---- Enabled Addons ----\n\n"); - - for (unsigned int i = 0 ; i < all_addons.size() ; i++) - { - const addon_info_t * info = &all_addons[i]; - - if (info->enabled) - fprintf(fp, "addon = %s\n", info->name); - } - - fprintf(fp, "\n"); -} - - -void VFS_ScanForAddons() -{ - LogPrintf("Scanning for addons....\n"); - - all_addons.clear(); - - char *dir_name = StringPrintf("%s/addons", install_dir); - - std::vector list; - int result = ScanDir_MatchingFiles(dir_name, "pk3", list); - - StringFree(dir_name); - - if (result < 0) - { - LogPrintf("FAILED -- missing folder??\n\n"); - return; - } - - for (unsigned int i = 0 ; i < list.size() ; i++) - { - addon_info_t info; - - info.name = StringDup(list[i].c_str()); - - info.enabled = false; - - if (initial_enabled_addons.find(list[i]) != initial_enabled_addons.end()) - info.enabled = true; - -//DEBUG -//info.enabled = true; - - LogPrintf(" found: %s%s\n", info.name, info.enabled ? " (Enabled)" : " (Disabled)"); - - all_addons.push_back(info); - - // if enabled, install into the VFS - if (info.enabled) - VFS_AddArchive(info.name, true /* options_file */); - } - - if (list.size() == 0) - LogPrintf("DONE (none found)\n"); - else - LogPrintf("DONE\n"); - - LogPrintf("\n"); -} - - -//---------------------------------------------------------------------- - -// -// this is useful to "extract" something out of virtual FS to the real -// file system so we can use normal stdio file operations on it -// [ especially a _library_ that uses stdio.h ] -// -bool VFS_CopyFile(const char *src_name, const char *dest_name) -{ - char buffer[1024]; - - PHYSFS_file *src = PHYSFS_openRead(src_name); - if (! src) - return false; - - FILE *dest = fopen(dest_name, "wb"); - if (! dest) - { - PHYSFS_close(src); - return false; - } - - bool was_OK = true; - - while (was_OK) - { - int rlen = (int)(PHYSFS_readBytes(src, buffer, sizeof(buffer)) / sizeof(buffer)); - if (rlen < 0) - was_OK = false; - - if (rlen <= 0) - break; - - int wlen = fwrite(buffer, 1, rlen, dest); - if (wlen < rlen || ferror(dest)) - was_OK = false; - } - - fclose(dest); - PHYSFS_close(src); - - return was_OK; -} - - -byte * VFS_LoadFile(const char *filename, int *length) -{ - *length = 0; - - PHYSFS_File *fp = PHYSFS_openRead(filename); - - if (! fp) - return NULL; - - *length = (int)PHYSFS_fileLength(fp); - - if (*length < 0) - { - PHYSFS_close(fp); - return NULL; - } - - byte *data = new byte[*length + 1]; - - // ensure buffer is NUL-terminated - data[*length] = 0; - - if ((PHYSFS_readBytes(fp, data, *length) / *length) != 1) - { - VFS_FreeFile(data); - PHYSFS_close(fp); - return NULL; - } - - PHYSFS_close(fp); - - return data; -} - - -void VFS_FreeFile(const byte *mem) -{ - if (mem) - { - delete[] mem; - } -} - - -//---------------------------------------------------------------------- - -class UI_Addon : public Fl_Group -{ -public: - addon_info_t *info; - - Fl_Check_Button *button; - -public: - UI_Addon(int x, int y, int w, int h, addon_info_t *_info) : - Fl_Group(x, y, w, h), - info(_info) - { - box(FL_THIN_UP_BOX); - - // prefix the name with a space - const char *name2 = StringPrintf(" %s", info->name); - - button = new Fl_Check_Button(x + kf_w(6), y + kf_h(4), w - kf_w(12), kf_h(24), name2); - // if (tip) - // button->tooltip(tip); - - end(); - - resizable(NULL); - } - - virtual ~UI_Addon() - { } - - int CalcHeight() const - { - return kf_h(34); - } - -}; - -//---------------------------------------------------------------------- - -class UI_AddonsWin : public Fl_Window -{ -public: - bool want_quit; - - Fl_Group *pack; - - Fl_Scrollbar *sbar; - - // area occupied by addon list - int mx, my, mw, mh; - - // number of pixels "lost" above the top - int offset_y; - - // total height of all shown addons - int total_h; - -public: - UI_AddonsWin(int W, int H, const char *label = NULL); - - virtual ~UI_AddonsWin() - { - // nothing needed - } - - void Populate(); - - bool ApplyChanges(); - - bool WantQuit() const - { - return want_quit; - } - -public: - // FLTK virtual method for handling input events. - int handle(int event); - -private: - void InsertAddon(addon_info_t *info); - - void PositionAll(); - - static void callback_Scroll(Fl_Widget *w, void *data) - { - - UI_AddonsWin *that = (UI_AddonsWin *)data; - - Fl_Scrollbar *sbar = (Fl_Scrollbar *)w; - - int previous_y = that->offset_y; - - that->offset_y = sbar->value(); - - int dy = that->offset_y - previous_y; - - // simply reposition all the UI_Module widgets - for (int j = 0; j < that->pack->children(); j++) - { - Fl_Widget *F = that->pack->child(j); - SYS_ASSERT(F); - - F->resize(F->x(), F->y() - dy, F->w(), F->h()); - } - - that->pack->redraw(); - } - - static void callback_Quit(Fl_Widget *w, void *data) - { - UI_AddonsWin *that = (UI_AddonsWin *)data; - - that->want_quit = true; - } -}; - - -// -// Constructor -// -UI_AddonsWin::UI_AddonsWin(int W, int H, const char *label) : - Fl_Window(W, H, label), - want_quit(false) -{ - callback(callback_Quit, this); - - // non-resizable - size_range(W, H, W, H); - - box(FL_FLAT_BOX); - - Fl_Color bg_col = alternate_look ? FL_DARK2 : FL_DARK3; - - color(bg_col, bg_col); - - -// int pad = kf_w(6); - - int dh = kf_h(64); - - - // area for addons list - mx = 0; - my = 0; - mw = W - Fl::scrollbar_size(); - mh = H - dh; - - offset_y = 0; - total_h = 0; - - - sbar = new Fl_Scrollbar(mx+mw, my, Fl::scrollbar_size(), mh); - sbar->callback(callback_Scroll, this); - - sbar->color(FL_DARK3+1, FL_DARK1); - - - const char *pack_title = StringPrintf("\n\n\n\n%s", _("List of Addons")); - - pack = new Fl_Group(mx, my, mw, mh, pack_title); - pack->clip_children(1); - pack->end(); - - pack->align(FL_ALIGN_INSIDE); - pack->labeltype(FL_NORMAL_LABEL); - pack->labelsize(FL_NORMAL_SIZE * 3 / 2); - - pack->box(FL_FLAT_BOX); - pack->color(bg_col); - pack->resizable(NULL); - - - //---------------- - - Fl_Group *darkish = new Fl_Group(0, H-dh, W, dh); - darkish->box(FL_FLAT_BOX); - darkish->color(WINDOW_BG, WINDOW_BG); - { - // finally add the close button - int bw = kf_w(60); - int bh = kf_h(30); - int bx = bw; - int by = H - dh/2 - bh/2 + 2; - - Fl_Button *apply_but = new Fl_Button(W-bx-bw, by, bw, bh, fl_close); - apply_but->callback(callback_Quit, this); - - // show warning about needing a restart - Fl_Box * sep = new Fl_Box(FL_NO_BOX, x(), by, W*3/5, bh, _("Changes require a restart")); - sep->align(FL_ALIGN_INSIDE); - sep->labelcolor(FL_DARK1); - sep->labelsize(small_font_size); - } - darkish->end(); - - end(); - - resizable(NULL); -} - - -int UI_AddonsWin::handle(int event) -{ - if (event == FL_KEYDOWN || event == FL_SHORTCUT) - { - int key = Fl::event_key(); - - switch (key) - { - case FL_Escape: - want_quit = true; - return 1; - - default: - break; - } - - // eat all other function keys - if (FL_F+1 <= key && key <= FL_F+12) - return 1; - } - - return Fl_Window::handle(event); -} - - -void UI_AddonsWin::PositionAll() -{ - // calculate new total height - int new_height = 0; - int spacing = 4; - - for (int k = 0 ; k < pack->children() ; k++) - { - UI_Addon *M = (UI_Addon *) pack->child(k); - SYS_ASSERT(M); - - if (M->visible()) - new_height += M->CalcHeight() + spacing; - } - - - // determine new offset_y - if (new_height <= mh) - { - offset_y = 0; - } - else - { - // when not shrinking, offset_y will remain valid - if (new_height < total_h) - offset_y = 0; - } - - total_h = new_height; - - SYS_ASSERT(offset_y >= 0); - SYS_ASSERT(offset_y <= total_h); - - - // reposition all the modules - int ny = my - offset_y; - - for (int j = 0 ; j < pack->children() ; j++) - { - UI_Addon *M = (UI_Addon *) pack->child(j); - SYS_ASSERT(M); - - int nh = M->visible() ? M->CalcHeight() : 1; - - if (ny != M->y() || nh != M->h()) - { - M->resize(M->x(), ny, M->w(), nh); - } - - if (M->visible()) - ny += M->CalcHeight() + spacing; - } - - - // p = position, first line displayed - // w = window, number of lines displayed - // t = top, number of first line - // l = length, total number of lines - sbar->value(offset_y, mh, 0, total_h); - - pack->redraw(); - -} - - -void UI_AddonsWin::InsertAddon(addon_info_t *info) -{ - UI_Addon *addon = new UI_Addon(mx, my, mw - 4, kf_h(34), info); - - if (info->enabled) - addon->button->value(1); - - pack->add(addon); - - PositionAll(); -} - - -void UI_AddonsWin::Populate() -{ - for (unsigned int i = 0 ; i < all_addons.size() ; i++) - { - InsertAddon(&all_addons[i]); - } -} - - -bool UI_AddonsWin::ApplyChanges() -{ - bool has_changes = false; - - for (int j = 0 ; j < pack->children() ; j++) - { - UI_Addon *M = (UI_Addon *) pack->child(j); - SYS_ASSERT(M); - - bool new_val = M->button->value() ? true : false; - - if (M->info->enabled != new_val) - { - has_changes = true; - M->info->enabled = new_val; - } - } - - return has_changes; -} - - -void DLG_SelectAddons(void) -{ - static UI_AddonsWin * addons_window = NULL; - - if (! addons_window) - { - int opt_w = kf_w(350); - int opt_h = kf_h(380); - - addons_window = new UI_AddonsWin(opt_w, opt_h, _("OBLIGE Addons")); - - addons_window->Populate(); - } - - addons_window->want_quit = false; - addons_window->set_modal(); - addons_window->show(); - - // run the GUI until the user closes - while (! addons_window->WantQuit()) - Fl::wait(); - - if (addons_window->ApplyChanges()) - { - // persist the changed addon list into OPTIONS.txt - Options_Save(options_file); - - fl_alert("%s", _("Changes to addons require a restart.\nOBLIGE will now close.")); - - main_action = MAIN_QUIT; - } - - addons_window->set_non_modal(); - addons_window->hide(); -} - -//--- editor settings --- -// vi:ts=4:sw=4:noexpandtab +//---------------------------------------------------------------------- +// Addons Loading and Selection GUI +//---------------------------------------------------------------------- +// +// Oblige Level Maker +// +// Copyright (C) 2006-2016 Andrew Apted +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//---------------------------------------------------------------------- + +#include "headers.h" +#include "hdr_fltk.h" +#include "hdr_ui.h" + +#include "physfs.h" + +#include "lib_argv.h" +#include "lib_file.h" +#include "lib_util.h" + +#include "main.h" +#include "m_addons.h" +#include "m_cookie.h" + + +// need this because the OPTIONS file is loaded *before* the addons +// folder is scanned for PK3 packages, so remember enabled ones here. +static std::map initial_enabled_addons; + +typedef struct +{ + const char *name; // base filename, includes ".pk3" extension + + bool enabled; + +} addon_info_t; + +static std::vector all_addons; + + +void VFS_AddFolder(const char *name) +{ + char *path = StringPrintf("%s/%s", install_dir, name); + char *mount = StringPrintf("/%s", name); + + if (! PHYSFS_mount(path, mount, 0)) + { + Main_FatalError("Failed to mount '%s' folder in PhysFS:\n%s\n", + name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return; /* NOT REACHED */ + } + + DebugPrintf("mounted folder '%s'\n", name); +} + +bool VFS_AddArchive(const char *filename, bool options_file) +{ + LogPrintf(" using: %s\n", filename); + + if (! HasExtension(filename)) + filename = ReplaceExtension(filename, "pk3"); + else + filename = StringDup(filename); + + // when handling "bare" filenames from the command line (i.e. ones + // containing no paths or drive spec) and the file does not exist in + // the current dir, look for it in the standard addons/ folder. + if (options_file || + (! FileExists(filename) && + filename == fl_filename_name(filename))) + { + char *new_name = StringPrintf("%s/addons/%s", home_dir, filename); + if (! FileExists(new_name)) + { + StringFree(new_name); + new_name = StringPrintf("%s/addons/%s", install_dir, filename); + } + StringFree(filename); + filename = new_name; + } + + if (! PHYSFS_mount(filename, "/", 0)) + { + if (options_file) + LogPrintf("Failed to mount '%s' archive in PhysFS:\n%s\n", + filename, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + else + Main_FatalError("Failed to mount '%s' archive in PhysFS:\n%s\n", + filename, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + + return false; + } + + return true; // Ok +} + +void VFS_InitAddons(const char *argv0) +{ + LogPrintf("Initializing VFS...\n"); + + if (! PHYSFS_init(argv0)) + { + Main_FatalError("Failed to init PhysFS:\n%s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return; /* NOT REACHED */ + } + + VFS_AddFolder("scripts"); + VFS_AddFolder("games"); + VFS_AddFolder("engines"); + VFS_AddFolder("modules"); + VFS_AddFolder("data"); + + LogPrintf("DONE.\n\n"); +} + + +void VFS_ParseCommandLine() +{ + int arg = ArgvFind('a', "addon"); + int count = 0; + + if (arg < 0) + return; + + arg++; + + LogPrintf("Command-line addons....\n"); + + for (; arg < arg_count && ! ArgvIsOption(arg) ; arg++, count++) + { + VFS_AddArchive(arg_list[arg], false /* options_file */); + } + + if (! count) + Main_FatalError("Missing filename for --addon option\n"); + + LogPrintf("DONE\n\n"); +} + + +void VFS_OptParse(const char *name) +{ + // just remember it now + if (initial_enabled_addons.find(name) == initial_enabled_addons.end()) + { + initial_enabled_addons[name] = 1; + } +} + + +void VFS_OptWrite(FILE *fp) +{ + fprintf(fp, "---- Enabled Addons ----\n\n"); + + for (unsigned int i = 0 ; i < all_addons.size() ; i++) + { + const addon_info_t * info = &all_addons[i]; + + if (info->enabled) + fprintf(fp, "addon = %s\n", info->name); + } + + fprintf(fp, "\n"); +} + + +void VFS_ScanForAddons() +{ + LogPrintf("Scanning for addons....\n"); + + all_addons.clear(); + + char *dir_name = StringPrintf("%s/addons", install_dir); + + std::vector list; + int result = ScanDir_MatchingFiles(dir_name, "pk3", list); + + StringFree(dir_name); + + if (result < 0) + { + LogPrintf("FAILED -- missing folder??\n\n"); + return; + } + + for (unsigned int i = 0 ; i < list.size() ; i++) + { + addon_info_t info; + + info.name = StringDup(list[i].c_str()); + + info.enabled = false; + + if (initial_enabled_addons.find(list[i]) != initial_enabled_addons.end()) + info.enabled = true; + +//DEBUG +//info.enabled = true; + + LogPrintf(" found: %s%s\n", info.name, info.enabled ? " (Enabled)" : " (Disabled)"); + + all_addons.push_back(info); + + // if enabled, install into the VFS + if (info.enabled) + VFS_AddArchive(info.name, true /* options_file */); + } + + if (list.size() == 0) + LogPrintf("DONE (none found)\n"); + else + LogPrintf("DONE\n"); + + LogPrintf("\n"); +} + + +//---------------------------------------------------------------------- + +// +// this is useful to "extract" something out of virtual FS to the real +// file system so we can use normal stdio file operations on it +// [ especially a _library_ that uses stdio.h ] +// +bool VFS_CopyFile(const char *src_name, const char *dest_name) +{ + char buffer[1024]; + + PHYSFS_file *src = PHYSFS_openRead(src_name); + if (! src) + return false; + + FILE *dest = fopen(dest_name, "wb"); + if (! dest) + { + PHYSFS_close(src); + return false; + } + + bool was_OK = true; + + while (was_OK) + { + int rlen = (int)(PHYSFS_readBytes(src, buffer, sizeof(buffer)) / sizeof(buffer)); + if (rlen < 0) + was_OK = false; + + if (rlen <= 0) + break; + + int wlen = fwrite(buffer, 1, rlen, dest); + if (wlen < rlen || ferror(dest)) + was_OK = false; + } + + fclose(dest); + PHYSFS_close(src); + + return was_OK; +} + + +byte * VFS_LoadFile(const char *filename, int *length) +{ + *length = 0; + + PHYSFS_File *fp = PHYSFS_openRead(filename); + + if (! fp) + return NULL; + + *length = (int)PHYSFS_fileLength(fp); + + if (*length < 0) + { + PHYSFS_close(fp); + return NULL; + } + + byte *data = new byte[*length + 1]; + + // ensure buffer is NUL-terminated + data[*length] = 0; + + if ((PHYSFS_readBytes(fp, data, *length) / *length) != 1) + { + VFS_FreeFile(data); + PHYSFS_close(fp); + return NULL; + } + + PHYSFS_close(fp); + + return data; +} + + +void VFS_FreeFile(const byte *mem) +{ + if (mem) + { + delete[] mem; + } +} + + +//---------------------------------------------------------------------- + +class UI_Addon : public Fl_Group +{ +public: + addon_info_t *info; + + Fl_Check_Button *button; + +public: + UI_Addon(int x, int y, int w, int h, addon_info_t *_info) : + Fl_Group(x, y, w, h), + info(_info) + { + box(FL_THIN_UP_BOX); + + // prefix the name with a space + const char *name2 = StringPrintf(" %s", info->name); + + button = new Fl_Check_Button(x + kf_w(6), y + kf_h(4), w - kf_w(12), kf_h(24), name2); + // if (tip) + // button->tooltip(tip); + + end(); + + resizable(NULL); + } + + virtual ~UI_Addon() + { } + + int CalcHeight() const + { + return kf_h(34); + } + +}; + +//---------------------------------------------------------------------- + +class UI_AddonsWin : public Fl_Window +{ +public: + bool want_quit; + + Fl_Group *pack; + + Fl_Scrollbar *sbar; + + // area occupied by addon list + int mx, my, mw, mh; + + // number of pixels "lost" above the top + int offset_y; + + // total height of all shown addons + int total_h; + +public: + UI_AddonsWin(int W, int H, const char *label = NULL); + + virtual ~UI_AddonsWin() + { + // nothing needed + } + + void Populate(); + + bool ApplyChanges(); + + bool WantQuit() const + { + return want_quit; + } + +public: + // FLTK virtual method for handling input events. + int handle(int event); + +private: + void InsertAddon(addon_info_t *info); + + void PositionAll(); + + static void callback_Scroll(Fl_Widget *w, void *data) + { + + UI_AddonsWin *that = (UI_AddonsWin *)data; + + Fl_Scrollbar *sbar = (Fl_Scrollbar *)w; + + int previous_y = that->offset_y; + + that->offset_y = sbar->value(); + + int dy = that->offset_y - previous_y; + + // simply reposition all the UI_Module widgets + for (int j = 0; j < that->pack->children(); j++) + { + Fl_Widget *F = that->pack->child(j); + SYS_ASSERT(F); + + F->resize(F->x(), F->y() - dy, F->w(), F->h()); + } + + that->pack->redraw(); + } + + static void callback_Quit(Fl_Widget *w, void *data) + { + UI_AddonsWin *that = (UI_AddonsWin *)data; + + that->want_quit = true; + } +}; + + +// +// Constructor +// +UI_AddonsWin::UI_AddonsWin(int W, int H, const char *label) : + Fl_Window(W, H, label), + want_quit(false) +{ + callback(callback_Quit, this); + + // non-resizable + size_range(W, H, W, H); + + box(FL_FLAT_BOX); + + Fl_Color bg_col = alternate_look ? FL_DARK2 : FL_DARK3; + + color(bg_col, bg_col); + + +// int pad = kf_w(6); + + int dh = kf_h(64); + + + // area for addons list + mx = 0; + my = 0; + mw = W - Fl::scrollbar_size(); + mh = H - dh; + + offset_y = 0; + total_h = 0; + + + sbar = new Fl_Scrollbar(mx+mw, my, Fl::scrollbar_size(), mh); + sbar->callback(callback_Scroll, this); + + sbar->color(FL_DARK3+1, FL_DARK1); + + + const char *pack_title = StringPrintf("\n\n\n\n%s", _("List of Addons")); + + pack = new Fl_Group(mx, my, mw, mh, pack_title); + pack->clip_children(1); + pack->end(); + + pack->align(FL_ALIGN_INSIDE); + pack->labeltype(FL_NORMAL_LABEL); + pack->labelsize(FL_NORMAL_SIZE * 3 / 2); + + pack->box(FL_FLAT_BOX); + pack->color(bg_col); + pack->resizable(NULL); + + + //---------------- + + Fl_Group *darkish = new Fl_Group(0, H-dh, W, dh); + darkish->box(FL_FLAT_BOX); + darkish->color(WINDOW_BG, WINDOW_BG); + { + // finally add the close button + int bw = kf_w(60); + int bh = kf_h(30); + int bx = bw; + int by = H - dh/2 - bh/2 + 2; + + Fl_Button *apply_but = new Fl_Button(W-bx-bw, by, bw, bh, fl_close); + apply_but->callback(callback_Quit, this); + + // show warning about needing a restart + Fl_Box * sep = new Fl_Box(FL_NO_BOX, x(), by, W*3/5, bh, _("Changes require a restart")); + sep->align(FL_ALIGN_INSIDE); + sep->labelcolor(FL_DARK1); + sep->labelsize(small_font_size); + } + darkish->end(); + + end(); + + resizable(NULL); +} + + +int UI_AddonsWin::handle(int event) +{ + if (event == FL_KEYDOWN || event == FL_SHORTCUT) + { + int key = Fl::event_key(); + + switch (key) + { + case FL_Escape: + want_quit = true; + return 1; + + default: + break; + } + + // eat all other function keys + if (FL_F+1 <= key && key <= FL_F+12) + return 1; + } + + return Fl_Window::handle(event); +} + + +void UI_AddonsWin::PositionAll() +{ + // calculate new total height + int new_height = 0; + int spacing = 4; + + for (int k = 0 ; k < pack->children() ; k++) + { + UI_Addon *M = (UI_Addon *) pack->child(k); + SYS_ASSERT(M); + + if (M->visible()) + new_height += M->CalcHeight() + spacing; + } + + + // determine new offset_y + if (new_height <= mh) + { + offset_y = 0; + } + else + { + // when not shrinking, offset_y will remain valid + if (new_height < total_h) + offset_y = 0; + } + + total_h = new_height; + + SYS_ASSERT(offset_y >= 0); + SYS_ASSERT(offset_y <= total_h); + + + // reposition all the modules + int ny = my - offset_y; + + for (int j = 0 ; j < pack->children() ; j++) + { + UI_Addon *M = (UI_Addon *) pack->child(j); + SYS_ASSERT(M); + + int nh = M->visible() ? M->CalcHeight() : 1; + + if (ny != M->y() || nh != M->h()) + { + M->resize(M->x(), ny, M->w(), nh); + } + + if (M->visible()) + ny += M->CalcHeight() + spacing; + } + + + // p = position, first line displayed + // w = window, number of lines displayed + // t = top, number of first line + // l = length, total number of lines + sbar->value(offset_y, mh, 0, total_h); + + pack->redraw(); + +} + + +void UI_AddonsWin::InsertAddon(addon_info_t *info) +{ + UI_Addon *addon = new UI_Addon(mx, my, mw - 4, kf_h(34), info); + + if (info->enabled) + addon->button->value(1); + + pack->add(addon); + + PositionAll(); +} + + +void UI_AddonsWin::Populate() +{ + for (unsigned int i = 0 ; i < all_addons.size() ; i++) + { + InsertAddon(&all_addons[i]); + } +} + + +bool UI_AddonsWin::ApplyChanges() +{ + bool has_changes = false; + + for (int j = 0 ; j < pack->children() ; j++) + { + UI_Addon *M = (UI_Addon *) pack->child(j); + SYS_ASSERT(M); + + bool new_val = M->button->value() ? true : false; + + if (M->info->enabled != new_val) + { + has_changes = true; + M->info->enabled = new_val; + } + } + + return has_changes; +} + + +void DLG_SelectAddons(void) +{ + static UI_AddonsWin * addons_window = NULL; + + if (! addons_window) + { + int opt_w = kf_w(350); + int opt_h = kf_h(380); + + addons_window = new UI_AddonsWin(opt_w, opt_h, _("OBLIGE Addons")); + + addons_window->Populate(); + } + + addons_window->want_quit = false; + addons_window->set_modal(); + addons_window->show(); + + // run the GUI until the user closes + while (! addons_window->WantQuit()) + Fl::wait(); + + if (addons_window->ApplyChanges()) + { + // persist the changed addon list into OPTIONS.txt + Options_Save(options_file); + + fl_alert("%s", _("Changes to addons require a restart.\nOBLIGE will now close.")); + + main_action = MAIN_QUIT; + } + + addons_window->set_non_modal(); + addons_window->hide(); +} + +//--- editor settings --- +// vi:ts=4:sw=4:noexpandtab \ No newline at end of file