summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--!NSTheme/!Boot,feb10
-rw-r--r--!NSTheme/!Help23
-rw-r--r--!NSTheme/!Run,feb20
-rw-r--r--!NSTheme/!Sprites,ff9bin0 -> 1580 bytes
-rw-r--r--!NSTheme/!Sprites22,ff9bin0 -> 3228 bytes
-rw-r--r--!NSTheme/5Sprites,ff9bin0 -> 3092 bytes
-rw-r--r--!NSTheme/5Sprites11,ff9bin0 -> 11132 bytes
-rw-r--r--!NSTheme/5Sprites22,ff9bin0 -> 4868 bytes
-rw-r--r--!NSTheme/ASprites,ff9bin0 -> 3664 bytes
-rw-r--r--!NSTheme/ASprites11,ff9bin0 -> 15892 bytes
-rw-r--r--!NSTheme/ASprites22,ff9bin0 -> 6092 bytes
-rw-r--r--!NSTheme/Resources/en/Messages67
-rw-r--r--!NSTheme/Resources/en/Templates,fecbin0 -> 2365 bytes
-rw-r--r--desktop/gui.h22
-rw-r--r--desktop/nstheme.c44
-rw-r--r--desktop/nstheme.h16
-rw-r--r--desktop/options.c149
-rw-r--r--desktop/options.h46
-rw-r--r--makefile71
-rw-r--r--posix.mk11
-rw-r--r--riscos.mk9
-rw-r--r--riscos/dialog.c479
-rw-r--r--riscos/gui.c608
-rw-r--r--riscos/gui.h97
-rw-r--r--riscos/help.c257
-rw-r--r--riscos/help.h20
-rw-r--r--riscos/menus.c272
-rw-r--r--riscos/options.h24
-rw-r--r--riscos/save.c554
-rw-r--r--riscos/wimp.c556
-rw-r--r--riscos/wimp.h51
-rw-r--r--utils/log.h26
-rw-r--r--utils/messages.c169
-rw-r--r--utils/messages.h29
-rw-r--r--utils/utils.c30
-rw-r--r--utils/utils.h23
36 files changed, 3683 insertions, 0 deletions
diff --git a/!NSTheme/!Boot,feb b/!NSTheme/!Boot,feb
new file mode 100644
index 0000000..633652a
--- /dev/null
+++ b/!NSTheme/!Boot,feb
@@ -0,0 +1,10 @@
+| Application system variables
+Set NSTheme$Help <Obey$Dir>.!Help
+Set NSTheme$Version "0.01 (04 Sep 2004)"
+Set NSTheme$Web "http://netsurf.sourceforge.net/"
+Set NSTheme$Title "NSTheme"
+Set NSTheme$Publisher "The NetSurf Developers"
+Set NSTheme$Description "NetSurf theme editor"
+
+| NSTheme Sprites
+If "<Boot$OSVersion>" < "436" Then IconSprites <Obey$Dir>.!Sprites Else If "<Boot$OSVersion>" < "500" Then IconSprites <Obey$Dir>.ASprites Else IconSprites <Obey$Dir>.5Sprites
diff --git a/!NSTheme/!Help b/!NSTheme/!Help
new file mode 100644
index 0000000..dbf32f9
--- /dev/null
+++ b/!NSTheme/!Help
@@ -0,0 +1,23 @@
+NSTheme
+=======
+This application allows the creation and editing of !NetSurf themes.
+
+
+
+Theme file format
+-----------------
+[+0] 'NSTM' (0x4d54534e)
+[+4] Minimum theme parser version required
+[+8] Theme name (32 chars, null terminated)
+[+40] Theme author (64 chars, null terminated)
+[+104] Browser toolbar background (0x00-0x0f)
+[+105] Hotlist toolbar background (0x00-0x0f)
+[+106] Status bar background (0x00-0x0f)
+[+107] Status bar foreground (0x00-0x0f)
+[+108] Throbber is on the right (0x00) or left (0xff)
+[+109] Reserved for future expansion (0x00)
+[+110] Reserved for future expansion (0x00)
+[+111] Reserved for future expansion (0x00)
+[+112] Size of compressed sprite data
+[+116] Size of uncompressed sprite data
+[+120] Start of compressed sprite data
diff --git a/!NSTheme/!Run,feb b/!NSTheme/!Run,feb
new file mode 100644
index 0000000..1ee70c4
--- /dev/null
+++ b/!NSTheme/!Run,feb
@@ -0,0 +1,20 @@
+<Obey$Dir>.!Boot
+
+| We need RISC OS 3.00 or greater
+RMEnsure UtilityModule 3.00 Error NSTheme required RISC OS 3.00 or later
+
+| Ensure CallASWI is installed
+| http://www.iyonix.com/32bit/
+RMEnsure UtilityModule 3.70 RMEnsure CallASWI 0.02 RMLoad System:Modules.CallASWI
+RMEnsure UtilityModule 3.70 RMEnsure CallASWI 0.02 Error NSTheme requires the CallASWI module. This can be downloaded from http://www.iyonix.com/32bit/system.shtml
+
+| Ensure SharedUnixLibrary is installed
+| http://www.chocky.org/unix/usage.html
+RMEnsure SharedUnixLibrary 1.02 RMLoad System:Modules.SharedULib
+RMEnsure SharedUnixLibrary 1.02 Error NSTheme requires SharedUnixLibrary 1.02 or later. This can be downloaded from http://www.chocky.org/unix/usage.html
+
+Wimpslot -min 160k -max 160k
+Set NSTheme$Dir <Obey$Dir>
+Set NSTheme$Running yes
+Run <Obey$Dir>.!RunImage 2><Obey$Dir>.stderr
+UnSet NSTheme$Running
diff --git a/!NSTheme/!Sprites,ff9 b/!NSTheme/!Sprites,ff9
new file mode 100644
index 0000000..afc643c
--- /dev/null
+++ b/!NSTheme/!Sprites,ff9
Binary files differ
diff --git a/!NSTheme/!Sprites22,ff9 b/!NSTheme/!Sprites22,ff9
new file mode 100644
index 0000000..ba77273
--- /dev/null
+++ b/!NSTheme/!Sprites22,ff9
Binary files differ
diff --git a/!NSTheme/5Sprites,ff9 b/!NSTheme/5Sprites,ff9
new file mode 100644
index 0000000..6daf777
--- /dev/null
+++ b/!NSTheme/5Sprites,ff9
Binary files differ
diff --git a/!NSTheme/5Sprites11,ff9 b/!NSTheme/5Sprites11,ff9
new file mode 100644
index 0000000..6bfaa62
--- /dev/null
+++ b/!NSTheme/5Sprites11,ff9
Binary files differ
diff --git a/!NSTheme/5Sprites22,ff9 b/!NSTheme/5Sprites22,ff9
new file mode 100644
index 0000000..2431eac
--- /dev/null
+++ b/!NSTheme/5Sprites22,ff9
Binary files differ
diff --git a/!NSTheme/ASprites,ff9 b/!NSTheme/ASprites,ff9
new file mode 100644
index 0000000..2c2728c
--- /dev/null
+++ b/!NSTheme/ASprites,ff9
Binary files differ
diff --git a/!NSTheme/ASprites11,ff9 b/!NSTheme/ASprites11,ff9
new file mode 100644
index 0000000..c0f822e
--- /dev/null
+++ b/!NSTheme/ASprites11,ff9
Binary files differ
diff --git a/!NSTheme/ASprites22,ff9 b/!NSTheme/ASprites22,ff9
new file mode 100644
index 0000000..13c0662
--- /dev/null
+++ b/!NSTheme/ASprites22,ff9
Binary files differ
diff --git a/!NSTheme/Resources/en/Messages b/!NSTheme/Resources/en/Messages
new file mode 100644
index 0000000..ed58fff
--- /dev/null
+++ b/!NSTheme/Resources/en/Messages
@@ -0,0 +1,67 @@
+# English messages for NSTheme
+
+# Menus
+NetSurf:NetSurf
+Info:Info
+AppHelp:Help... F1
+Quit:Quit
+SaveAs:Save
+Export:Export
+SaveTitle:Save as
+ExportTitle:Export sprites
+
+# Icon definitions
+back:browser back icon
+forward:browser forward icon
+stop:browser stop icon
+reload:browser reload icon
+home:browser home icon
+search:browser search icon
+history:browser history icon
+scale:browser scale icon
+hotlist:browser hotlist icon
+save:browser save icon
+print:browser print icon
+create:hotlist create entry icon
+delete:hotlist delete entry icon
+launch:hotlist launch icon
+open:hotlist open directories icon
+expand:hotlist expand entries icon
+separator:toolbar separator
+pushed: (pushed)
+
+# Report text
+Title:NSTheme Sprites Report
+WarnNoFile:Warning: No sprite file present.
+WarnAlphaSpr:Warning: Sprite '%s' has an alpha channel specified.
+WarnHighSpr:Warning: Sprite '%s' uses more than 256 colours.
+WarnNoSpr:Warning: Sprite '%s' (%s%s) is missing.
+WarnExtraSpr:Warning: Sprite '%s' is redundant.
+WarnNoThrob:Warning: No throbber animation present.
+WarnMissThrob:Warning: Throbber frame '%s' is missing.
+CompleteErr:Analysis completed with %i warning(s).
+CompleteOK:Analysis completed with no warnings.
+
+# Interactive help
+HelpMain:\TNSTheme editing window.|MDrag a sprite file here to load it into the theme.
+HelpMain3:\bwritable field specifies the theme name.
+HelpMain5:\bwritable field specifies the theme author.
+HelpMain8:\btick-box controls whether the theme throbber is on the left.
+HelpMain11:\Sto view a report on the current sprites.
+HelpMain12:\Sto remove the current sprites.
+HelpMain16:\bis the current colour of the browser toolbar background.
+HelpMain17:\Sto select the colour of the browser toolbar background.
+HelpMain19:\bis the current colour of the hotlist toolbar background.
+HelpMain20:\Sto select the colour of the hotlist toolbar background.
+HelpMain22:\bis the current colour of the status bar background.
+HelpMain23:\Sto select the colour of the status bar background.
+HelpMain25:\bis the current colour of the status bar text.
+HelpMain26:\Sto select the colour of the status bar text.
+HelpAppInfo:\TNSTheme information \w.
+HelpMainMenu0:\Rto save the theme.
+HelpMainMenu1:\Rto export the theme sprites.
+HelpMainMenu2:\Sview NSTheme's documentation.
+HelpIconMenu0:\Rview information about this software.
+HelpIconMenu1:\Sview NSTheme's documentation.
+HelpIconMenu2:\Squit NSTheme.
+HelpIconbar:\TNSTheme icon. \ No newline at end of file
diff --git a/!NSTheme/Resources/en/Templates,fec b/!NSTheme/Resources/en/Templates,fec
new file mode 100644
index 0000000..24e34a5
--- /dev/null
+++ b/!NSTheme/Resources/en/Templates,fec
Binary files differ
diff --git a/desktop/gui.h b/desktop/gui.h
new file mode 100644
index 0000000..f4069c1
--- /dev/null
+++ b/desktop/gui.h
@@ -0,0 +1,22 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * Interface to platform-specific gui functions.
+ */
+
+#ifndef _NSTHEME_DESKTOP_GUI_H_
+#define _NSTHEME_DESKTOP_GUI_H_
+
+#include <stdbool.h>
+
+void gui_init(void);
+void gui_multitask(void);
+void gui_poll(void);
+void gui_exit(void);
+
+#endif
diff --git a/desktop/nstheme.c b/desktop/nstheme.c
new file mode 100644
index 0000000..d4e5a19
--- /dev/null
+++ b/desktop/nstheme.c
@@ -0,0 +1,44 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+#include <stdbool.h>
+#include "nstheme/desktop/gui.h"
+#include "nstheme/desktop/nstheme.h"
+#include "nstheme/utils/utils.h"
+
+bool application_quit = false;
+
+static void application_init(void);
+static void application_exit(void);
+
+
+/**
+ * NSTheme main().
+ */
+int main(int argc, char** argv) {
+ application_init();
+ while (!application_quit) gui_poll();
+ application_exit();
+ return EXIT_SUCCESS;
+}
+
+
+/**
+ * Initialise application.
+ */
+void application_init(void) {
+ stdout = stderr;
+ gui_init();
+}
+
+
+/**
+ * Finalise application.
+ */
+void application_exit(void) {
+ gui_exit();
+}
diff --git a/desktop/nstheme.h b/desktop/nstheme.h
new file mode 100644
index 0000000..fab28d3
--- /dev/null
+++ b/desktop/nstheme.h
@@ -0,0 +1,16 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+#ifndef _NSTHEME_DESKTOP_NSTHEME_H_
+#define _NSTHEME_DESKTOP_NSTHEME_H_
+
+#include <stdbool.h>
+
+extern bool application_quit;
+
+#endif
+
diff --git a/desktop/options.c b/desktop/options.c
new file mode 100644
index 0000000..b43291b
--- /dev/null
+++ b/desktop/options.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * Option reading and saving (implementation).
+ *
+ * Options are stored in the format key:value, one per line. For bool options,
+ * value is "0" or "1".
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "nstheme/desktop/options.h"
+#include "nstheme/utils/log.h"
+#include "nstheme/utils/utils.h"
+
+#ifdef riscos
+#include "nstheme/riscos/options.h"
+#else
+#define EXTRA_OPTION_DEFINE
+#define EXTRA_OPTION_TABLE
+#endif
+
+
+EXTRA_OPTION_DEFINE
+
+
+struct {
+ const char *key;
+ enum { OPTION_BOOL, OPTION_INTEGER, OPTION_STRING } type;
+ void *p;
+} option_table[] = {
+ EXTRA_OPTION_TABLE
+};
+
+#define option_table_entries (sizeof option_table / sizeof option_table[0])
+
+
+/**
+ * Read options from a file.
+ *
+ * \param path name of file to read options from
+ *
+ * Option variables corresponding to lines in the file are updated. Missing
+ * options are unchanged. If the file fails to open, options are unchanged.
+ */
+
+void options_read(const char *path)
+{
+ char s[100];
+ FILE *fp;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ LOG(("failed to open file '%s'", path));
+ return;
+ }
+
+ while (fgets(s, 100, fp)) {
+ char *colon, *value;
+ unsigned int i;
+
+ if (s[0] == 0 || s[0] == '#')
+ continue;
+ colon = strchr(s, ':');
+ if (colon == 0)
+ continue;
+ s[strlen(s) - 1] = 0; /* remove \n at end */
+ *colon = 0; /* terminate key */
+ value = colon + 1;
+
+ for (i = 0; i != option_table_entries; i++) {
+ if (strcasecmp(s, option_table[i].key) != 0)
+ continue;
+
+ switch (option_table[i].type) {
+ case OPTION_BOOL:
+ *((bool *) option_table[i].p) =
+ value[0] == '1';
+ break;
+
+ case OPTION_INTEGER:
+ *((int *) option_table[i].p) =
+ atoi(value);
+ break;
+
+ case OPTION_STRING:
+ free(*((char **) option_table[i].p));
+ *((char **) option_table[i].p) =
+ strdup(value);
+ break;
+ }
+ break;
+ }
+ }
+
+ fclose(fp);
+}
+
+
+/**
+ * Save options to a file.
+ *
+ * \param path name of file to write options to
+ *
+ * Errors are ignored.
+ */
+
+void options_write(const char *path)
+{
+ unsigned int i;
+ FILE *fp;
+
+ fp = fopen(path, "w");
+ if (!fp) {
+ LOG(("failed to open file '%s' for writing", path));
+ return;
+ }
+
+ for (i = 0; i != option_table_entries; i++) {
+ fprintf(fp, "%s:", option_table[i].key);
+ switch (option_table[i].type) {
+ case OPTION_BOOL:
+ fprintf(fp, "%c", *((bool *) option_table[i].p) ?
+ '1' : '0');
+ break;
+
+ case OPTION_INTEGER:
+ fprintf(fp, "%i", *((int *) option_table[i].p));
+ break;
+
+ case OPTION_STRING:
+ if (*((char **) option_table[i].p))
+ fprintf(fp, "%s", *((char **) option_table[i].p));
+ break;
+ }
+ fprintf(fp, "\n");
+ }
+
+ fclose(fp);
+}
diff --git a/desktop/options.h b/desktop/options.h
new file mode 100644
index 0000000..ae35cda
--- /dev/null
+++ b/desktop/options.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ */
+
+/** \file
+ * Option reading and saving (interface).
+ *
+ * Non-platform specific options can be added by editing this file and
+ * netsurf/desktop/options.c
+ *
+ * Platform specific options should be added in the platform options.h.
+ *
+ * The following types of options are supported:
+ * - bool (OPTION_BOOL)
+ * - int (OPTION_INTEGER)
+ * - char* (OPTION_STRING) (must be allocated on heap, may be 0, free before
+ * assigning a new value)
+ */
+
+#ifndef _NETSURF_DESKTOP_OPTIONS_H_
+#define _NETSURF_DESKTOP_OPTIONS_H_
+
+enum { OPTION_HTTP_PROXY_AUTH_NONE = 0, OPTION_HTTP_PROXY_AUTH_BASIC = 1,
+ OPTION_HTTP_PROXY_AUTH_NTLM = 2 };
+
+extern bool option_http_proxy;
+extern char *option_http_proxy_host;
+extern int option_http_proxy_port;
+extern int option_http_proxy_auth;
+extern char *option_http_proxy_auth_user;
+extern char *option_http_proxy_auth_pass;
+extern int option_font_size;
+extern int option_font_min_size;
+extern char *option_accept_language;
+extern bool option_ssl_verify_certificates;
+extern int option_memory_cache_size;
+extern bool option_block_ads;
+
+void options_read(const char *path);
+void options_write(const char *path);
+
+#endif
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..71bb865
--- /dev/null
+++ b/makefile
@@ -0,0 +1,71 @@
+#
+# This file is part of NetSurf, http://netsurf.sourceforge.net/
+# Licensed under the GNU General Public License,
+# http://www.opensource.org/licenses/gpl-license
+#
+
+# There is 1 possible build of NSTheme:
+#
+# riscos -- standard RISC OS build
+#
+# "riscos" can be compiled under RISC OS, or cross-compiled using gccsdk.
+
+OBJECTS_COMMON = messages.o utils.o # utils/
+OBJECTS_COMMON += nstheme.o options.o # desktop/
+
+OBJECTS_RISCOS = $(OBJECTS_COMMON)
+OBJECTS_RISCOS += dialog.o gui.o help.o menus.o wimp.o save.o
+
+
+OBJDIR_RISCOS = $(shell $(CC) -dumpmachine)
+SOURCES_RISCOS=$(OBJECTS_RISCOS:.o=.c)
+OBJS_RISCOS=$(OBJECTS_RISCOS:%.o=$(OBJDIR_RISCOS)/%.o)
+
+# Inclusion of platform specific files has to occur after the OBJDIR stuff as
+# that is refered to in the files
+
+OS = $(word 2,$(subst -, ,$(shell gcc -dumpmachine)))
+ifeq ($(OS),riscos)
+include riscos.mk
+else
+include posix.mk
+endif
+
+VPATH = desktop:riscos:utils
+
+WARNFLAGS = -W -Wall -Wundef -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wstrict-prototypes \
+ -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls \
+ -Wnested-externs -Winline -Wno-unused-parameter -Wuninitialized
+
+# CFLAGS have to appear after the inclusion of platform specific files as the
+# PLATFORM_CFLAGS variables are defined in them
+
+CFLAGS_RISCOS = -std=c9x -D_BSD_SOURCE -Driscos -DBOOL_DEFINED -O2 \
+ $(WARNFLAGS) -I.. $(PLATFORM_CFLAGS_RISCOS) -mpoke-function-name \
+
+# targets
+riscos: $(RUNIMAGE)
+$(RUNIMAGE) : $(OBJS_RISCOS)
+ $(CC) -o $@ $(LDFLAGS_RISCOS) $^
+
+netsurf.zip: $(RUNIMAGE)
+ rm nstheme.zip; riscos-zip -9vr, nstheme.zip !NSTheme
+
+# pattern rule for c source
+$(OBJDIR_RISCOS)/%.o : %.c
+ @echo "==> $<"
+ @$(CC) -o $@ -c $(CFLAGS_RISCOS) $<
+
+# generate dependencies
+depend : $(SOURCES_RISCOS)
+ -mkdir $(OBJDIR_RISCOS)
+ $(CC) -MM -MG $(CFLAGS_RISCOS) $^ | sed 's|.*\.o:|$(OBJDIR_RISCOS)/&|g' > depend
+
+# remove generated files
+clean :
+ -rm $(OBJDIR_RISCOS)
+
+ifneq ($(OS),riscos)
+include depend
+endif
diff --git a/posix.mk b/posix.mk
new file mode 100644
index 0000000..1734364
--- /dev/null
+++ b/posix.mk
@@ -0,0 +1,11 @@
+CC = /riscos/bin/gcc
+CC_DEBUG = gcc
+
+PLATFORM_CFLAGS_RISCOS =
+PLATFORM_CFLAGS_DEBUG = -I/usr/include/libxml2 -I/riscos/src/OSLib \
+ -I/riscos/include/libjpeg -D_POSIX_C_SOURCE
+
+LDFLAGS_RISCOS = -L/riscos/lib -lxml2 -lz -lcurl -lssl -lcrypto -lcares -lmng \
+ -loslib -ljpeg
+
+RUNIMAGE = !NSTheme/!RunImage,ff8
diff --git a/riscos.mk b/riscos.mk
new file mode 100644
index 0000000..729f43f
--- /dev/null
+++ b/riscos.mk
@@ -0,0 +1,9 @@
+CC = gcc
+CC_DEBUG = gcc
+
+PLATFORM_CFLAGS_RISCOS = -INSLibs:include -IOSLib:
+PLATFORM_CFLAGS_DEBUG = -INSLibs:include -IOSLib:
+
+LDFLAGS_RISCOS = OSLib:o.oslib32
+
+RUNIMAGE = !NSTheme/!RunImage
diff --git a/riscos/dialog.c b/riscos/dialog.c
new file mode 100644
index 0000000..0780992
--- /dev/null
+++ b/riscos/dialog.c
@@ -0,0 +1,479 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/colourtrans.h"
+#include "oslib/os.h"
+#include "oslib/osfile.h"
+#include "oslib/osgbpb.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+#include "nstheme/desktop/nstheme.h"
+#include "nstheme/riscos/gui.h"
+#include "nstheme/riscos/options.h"
+#include "nstheme/riscos/wimp.h"
+#include "nstheme/utils/log.h"
+#include "nstheme/utils/messages.h"
+#include "nstheme/utils/utils.h"
+
+
+osspriteop_area *theme_sprites = NULL;
+
+const char *theme_sprite_name[] = {
+ "back", "pback",
+ "forward", "pforward",
+ "stop", "pstop",
+ "reload", "preload",
+ "home", "phome",
+ "search", "psearch",
+ "history", "phistory",
+ "scale", "pscale",
+ "hotlist", "photlist",
+ "save", "psave",
+ "print", "pprint",
+ "create", "pcreate",
+ "delete", "pdelete",
+ "launch", "plaunch",
+ "open", "popen",
+ "expand", "pexpand",
+ "separator",
+ NULL
+};
+
+
+wimp_w dialog_info, dialog_main, dialog_saveas, dialog_warning;
+
+static void ro_gui_dialog_click_main(wimp_pointer *pointer);
+static void ro_gui_dialog_click_warning(wimp_pointer *pointer);
+static wimp_w ro_gui_dialog_create(const char *template_name);
+static wimp_window * ro_gui_dialog_load_template(const char *template_name);
+static void ro_gui_dialog_main_report(void);
+static unsigned int ro_gui_dialog_test_sprite(const char *name);
+
+
+/**
+ * Load and create dialogs from template file.
+ */
+
+void ro_gui_dialog_init(void)
+{
+ dialog_info = ro_gui_dialog_create("info");
+ dialog_main = ro_gui_dialog_create("main");
+ dialog_saveas = ro_gui_dialog_create("saveas");
+ dialog_warning = ro_gui_dialog_create("warning");
+ ro_gui_set_icon_string(dialog_main, ICON_MAIN_NAME, "\0");
+ ro_gui_set_icon_string(dialog_main, ICON_MAIN_AUTHOR, "\0");
+}
+
+
+/**
+ * Load a template without creating a window.
+ *
+ * \param template_name name of template to load
+ * \return window block
+ *
+ * Exits through die() on error.
+ */
+
+wimp_window * ro_gui_dialog_load_template(const char *template_name)
+{
+ char name[20];
+ int context, window_size, data_size;
+ char *data;
+ wimp_window *window;
+ os_error *error;
+
+ /* wimp_load_template won't accept a const char * */
+ strncpy(name, template_name, sizeof name);
+
+ /* find required buffer sizes */
+ error = xwimp_load_template(wimp_GET_SIZE, 0, 0, wimp_NO_FONTS,
+ name, 0, &window_size, &data_size, &context);
+ if (error) {
+ LOG(("xwimp_load_template: 0x%x: %s",
+ error->errnum, error->errmess));
+ xwimp_close_template();
+ die(error->errmess);
+ }
+ if (!context) {
+ LOG(("template '%s' missing", template_name));
+ xwimp_close_template();
+ die("Template");
+ }
+
+ /* allocate space for indirected data and temporary window buffer */
+ data = malloc(data_size);
+ window = malloc(window_size);
+ if (!data || !window) {
+ xwimp_close_template();
+ die("NoMemory");
+ }
+
+ /* load template */
+ error = xwimp_load_template(window, data, data + data_size,
+ wimp_NO_FONTS, name, 0, 0, 0, 0);
+ if (error) {
+ LOG(("xwimp_load_template: 0x%x: %s",
+ error->errnum, error->errmess));
+ xwimp_close_template();
+ die(error->errmess);
+ }
+
+ return window;
+}
+
+
+/**
+ * Create a window from a template.
+ *
+ * \param template_name name of template to load
+ * \return window handle
+ *
+ * Exits through die() on error.
+ */
+
+wimp_w ro_gui_dialog_create(const char *template_name)
+{
+ wimp_window *window;
+ wimp_w w;
+ os_error *error;
+
+ window = ro_gui_dialog_load_template(template_name);
+
+ /* create window */
+ error = xwimp_create_window(window, &w);
+ if (error) {
+ LOG(("xwimp_create_window: 0x%x: %s",
+ error->errnum, error->errmess));
+ xwimp_close_template();
+ die(error->errmess);
+ }
+
+ /* the window definition is copied by the wimp and may be freed */
+ free(window);
+
+ return w;
+}
+
+
+/**
+ * Open a dialog box, centered on the screen.
+ */
+
+void ro_gui_dialog_open(wimp_w w)
+{
+ int screen_x, screen_y, dx, dy;
+ wimp_window_state open;
+ os_error *error;
+
+ /* find screen centre in os units */
+ ro_gui_screen_size(&screen_x, &screen_y);
+ screen_x /= 2;
+ screen_y /= 2;
+
+ /* centre and open */
+ open.w = w;
+ error = xwimp_get_window_state(&open);
+ if (error) {
+ LOG(("xwimp_get_window_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+ dx = (open.visible.x1 - open.visible.x0) / 2;
+ dy = (open.visible.y1 - open.visible.y0) / 2;
+ open.visible.x0 = screen_x - dx;
+ open.visible.x1 = screen_x + dx;
+ open.visible.y0 = screen_y - dy;
+ open.visible.y1 = screen_y + dy;
+ open.next = wimp_TOP;
+ error = xwimp_open_window((wimp_open *) &open);
+ if (error) {
+ LOG(("xwimp_open_window: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Set the caret position
+ */
+ ro_gui_set_caret_first(w);
+}
+
+
+
+
+/**
+ * Handle key presses in one of the dialog boxes.
+ */
+
+bool ro_gui_dialog_keypress(wimp_key *key) {
+ if (key->c == wimp_KEY_ESCAPE) {
+ ro_gui_dialog_close(key->w);
+ return true;
+ } else if (key->c == wimp_KEY_RETURN) {
+/* if ((key->w == dialog_folder) || (key->w == dialog_entry)) {
+ return true;
+ }
+*/ }
+ return false;
+}
+
+
+/**
+ * Handle clicks in one of the dialog boxes.
+ */
+
+void ro_gui_dialog_click(wimp_pointer *pointer)
+{
+ if (pointer->buttons == wimp_CLICK_MENU) {
+ if (pointer->w == dialog_main) {
+ ro_gui_create_menu(main_menu, pointer->pos.x,
+ pointer->pos.y);
+ }
+ return;
+ }
+
+ if (pointer->w == dialog_main)
+ ro_gui_dialog_click_main(pointer);
+ else if (pointer->w == dialog_warning)
+ ro_gui_dialog_click_warning(pointer);
+}
+
+
+/**
+ * Handle clicks in the main dialog.
+ */
+
+void ro_gui_dialog_click_main(wimp_pointer *pointer) {
+ if (pointer->i == ICON_MAIN_BROWSER_MENU) {
+ ro_gui_popup_menu(colour_menu, dialog_main,
+ ICON_MAIN_BROWSER_MENU);
+ } else if (pointer->i == ICON_MAIN_HOTLIST_MENU) {
+ ro_gui_popup_menu(colour_menu, dialog_main,
+ ICON_MAIN_HOTLIST_MENU);
+ } else if (pointer->i == ICON_MAIN_STATUSBG_MENU) {
+ ro_gui_popup_menu(colour_menu, dialog_main,
+ ICON_MAIN_STATUSBG_MENU);
+ } else if (pointer->i == ICON_MAIN_STATUSFG_MENU) {
+ ro_gui_popup_menu(colour_menu, dialog_main,
+ ICON_MAIN_STATUSFG_MENU);
+ } else if (pointer->i == ICON_MAIN_REPORT) {
+ ro_gui_dialog_main_report();
+ } else if (pointer->i == ICON_MAIN_REMOVE) {
+ if (theme_sprites) {
+ free(theme_sprites);
+ theme_sprites = NULL;
+ ro_gui_dialog_prepare_main();
+ }
+ }
+}
+
+
+/**
+ * Handle clicks in the warning dialog.
+ */
+
+void ro_gui_dialog_click_warning(wimp_pointer *pointer)
+{
+ if (pointer->i == ICON_WARNING_CONTINUE)
+ ro_gui_dialog_close(dialog_warning);
+}
+
+
+/**
+ * Close a dialog box.
+ */
+
+void ro_gui_dialog_close(wimp_w close) {
+ os_error *error;
+ error = xwimp_close_window(close);
+ if (error) {
+ LOG(("xwimp_close_window: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ }
+}
+
+void ro_gui_dialog_prepare_main(void) {
+ ro_gui_set_icon_shaded_state(dialog_main, ICON_MAIN_REMOVE,
+ (theme_sprites == NULL));
+
+}
+
+void ro_gui_dialog_main_report(void) {
+ char name[16];
+ char name2[16];
+ const char *lookup_name;
+ const char *pushed_name;
+ unsigned int i;
+ int j, n;
+ unsigned int err;
+ int warn_count = 0;
+ bool found;
+ int throb_frames = -1;
+ FILE *fp;
+ fp = fopen("<Wimp$Scrap>", "w");
+ if (!fp) {
+ warn_user("ReportError", 0);
+ LOG(("failed to open file '<Wimp$Scrap>' for writing"));
+ return;
+ }
+ fprintf(fp, messages_get("Title"));
+ fprintf(fp, "\n");
+ for (i = 0; i < strlen(messages_get("Title")); i++) {
+ fprintf(fp, "=");
+ }
+ fprintf(fp, "\n");
+ if (theme_sprites) {
+ i = 0;
+ while (theme_sprite_name[i]) {
+ err = ro_gui_dialog_test_sprite(theme_sprite_name[i]);
+ if (err & 1) {
+ warn_count++;
+ lookup_name = messages_get(theme_sprite_name[i & ~1]);
+ if (i & 1) {
+ pushed_name = messages_get("pushed");
+ } else {
+ pushed_name = "";
+ }
+ fprintf(fp, messages_get("WarnNoSpr"),
+ theme_sprite_name[i],
+ lookup_name, pushed_name);
+ fprintf(fp, "\n");
+ }
+ if (err & 2) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnHighSpr"),
+ theme_sprite_name[i]);
+ fprintf(fp, "\n");
+ }
+ if (err & 4) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnAlphaSpr"),
+ theme_sprite_name[i]);
+ fprintf(fp, "\n");
+ }
+ i++;
+ }
+ for (j = 1; j <= theme_sprites->sprite_count; j++) {
+ found = false;
+ osspriteop_return_name(osspriteop_USER_AREA,
+ theme_sprites, name, 16, j);
+ if (strncmp(name, "throbber", 8) == 0) {
+ n = atoi(name + 8);
+ sprintf(name2, "throbber%i", n);
+ if (strcmp(name, name2) == 0) {
+ found = true;
+ throb_frames = n;
+ }
+ } else {
+ i = 0;
+ while (!found && theme_sprite_name[i]) {
+ if (strcmp(name, theme_sprite_name[i]) == 0) {
+ found = true;
+ }
+ i++;
+ }
+ }
+
+ if (!found) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnExtraSpr"),
+ name);
+ fprintf(fp, "\n");
+ err = ro_gui_dialog_test_sprite(name);
+ if (err & 2) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnHighSpr"),
+ name);
+ fprintf(fp, "\n");
+ }
+ if (err & 4) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnAlphaSpr"),
+ name);
+ fprintf(fp, "\n");
+ }
+ }
+ }
+ if (throb_frames == -1) {
+ fprintf(fp, messages_get("WarnNoThrob"));
+ fprintf(fp, "\n");
+
+ } else {
+ for (j = 0; j < throb_frames; j++) {
+ sprintf(name, "throbber%i", j);
+ err = ro_gui_dialog_test_sprite(name);
+ if (err & 1) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnMissThrob"),
+ name);
+ fprintf(fp, "\n");
+ }
+ if (err & 2) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnHighSpr"),
+ name);
+ fprintf(fp, "\n");
+ }
+ if (err & 4) {
+ warn_count++;
+ fprintf(fp, messages_get("WarnAlphaSpr"),
+ name);
+ fprintf(fp, "\n");
+ }
+ }
+ }
+
+ } else {
+ warn_count++;
+ fprintf(fp, messages_get("WarnNoFile"));
+ fprintf(fp, "\n");
+ }
+ if (warn_count > 0) {
+ fprintf(fp, "\n");
+ fprintf(fp, messages_get("CompleteErr"), warn_count);
+ fprintf(fp, "\n");
+ } else {
+ fprintf(fp, messages_get("CompleteOK"));
+ fprintf(fp, "\n");
+ }
+
+ fclose(fp);
+ xosfile_set_type("<Wimp$Scrap>", 0xfff);
+ xos_cli("Filer_Run <Wimp$Scrap>");
+}
+
+unsigned int ro_gui_dialog_test_sprite(const char *name) {
+ unsigned int result = 0;
+ int var_val;
+ os_coord dimensions;
+ os_mode mode;
+ os_error *error;
+ error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
+ theme_sprites, (osspriteop_id)name,
+ &dimensions.x, &dimensions.y, 0, &mode);
+ if (error) return 1;
+ if (((unsigned int)mode) & osspriteop_ALPHA_MASK) result |= 4;
+ error = xos_read_mode_variable(mode, os_MODEVAR_LOG2_BPP,
+ &var_val, 0);
+ if (error) {
+ LOG(("xos_read_mode_variable: 0x%x: %s",
+ error->errnum, error->errmess));
+ } else {
+ if (var_val > 3) result |= 2;
+ }
+ return result;
+}
diff --git a/riscos/gui.c b/riscos/gui.c
new file mode 100644
index 0000000..be5dae3
--- /dev/null
+++ b/riscos/gui.c
@@ -0,0 +1,608 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unixlib/features.h>
+#include <unixlib/local.h>
+#include "oslib/font.h"
+#include "oslib/help.h"
+#include "oslib/hourglass.h"
+#include "oslib/inetsuite.h"
+#include "oslib/os.h"
+#include "oslib/osbyte.h"
+#include "oslib/osfile.h"
+#include "oslib/osfscontrol.h"
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpspriteop.h"
+#include "oslib/uri.h"
+#include "nstheme/desktop/gui.h"
+#include "nstheme/desktop/nstheme.h"
+#include "nstheme/desktop/options.h"
+#include "nstheme/riscos/gui.h"
+#include "nstheme/riscos/help.h"
+#include "nstheme/riscos/options.h"
+//#include "nstheme/riscos/theme.h"
+#include "nstheme/riscos/wimp.h"
+#include "nstheme/utils/log.h"
+#include "nstheme/utils/messages.h"
+#include "nstheme/utils/utils.h"
+
+const char *__dynamic_da_name = "NSTheme"; /**< For UnixLib. */
+int __feature_imagefs_is_file = 1; /**< For UnixLib. */
+/* default filename handling */
+int __riscosify_control = __RISCOSIFY_NO_SUFFIX |
+ __RISCOSIFY_NO_REVERSE_SUFFIX;
+
+char *NSTHEME_DIR;
+
+wimp_t task_handle; /**< RISC OS wimp task handle. */
+static clock_t gui_last_poll; /**< Time of last wimp_poll. */
+
+/** Accepted wimp user messages. */
+static wimp_MESSAGE_LIST(33) task_messages = { {
+ message_HELP_REQUEST,
+ message_DATA_SAVE,
+ message_DATA_SAVE_ACK,
+ message_DATA_LOAD,
+ message_DATA_LOAD_ACK,
+ message_MENU_WARNING,
+ 0
+} };
+struct ro_gui_poll_block {
+ wimp_event_no event;
+ wimp_block *block;
+ struct ro_gui_poll_block *next;
+};
+struct ro_gui_poll_block *ro_gui_poll_queued_blocks = 0;
+
+
+static void ro_gui_choose_language(void);
+static void ro_gui_icon_bar_create(void);
+static void ro_gui_handle_event(wimp_event_no event, wimp_block *block);
+static void ro_gui_poll_queue(wimp_event_no event, wimp_block *block);
+static void ro_gui_open_window_request(wimp_open *open);
+static void ro_gui_close_window_request(wimp_close *close);
+static void ro_gui_mouse_click(wimp_pointer *pointer);
+static void ro_gui_icon_bar_click(wimp_pointer *pointer);
+static void ro_gui_drag_end(wimp_dragged *drag);
+static void ro_gui_keypress(wimp_key *key);
+static void ro_gui_user_message(wimp_event_no event, wimp_message *message);
+static void ro_msg_dataload(wimp_message *block);
+static void ro_msg_datasave_ack(wimp_message *message);
+
+
+/**
+ * Initialise the gui (RISC OS specific part).
+ */
+
+void gui_init(void) {
+ char path[40];
+ os_error *error;
+ int length;
+
+ xhourglass_start(1);
+
+ options_read("Choices:NSTheme.Choices");
+ ro_gui_choose_language();
+
+ NSTHEME_DIR = getenv("NSTheme$Dir");
+ if ((length = snprintf(path, sizeof(path),
+ "<NSTheme$Dir>.Resources.%s.Messages",
+ option_language)) < 0 || length >= (int)sizeof(path))
+ die("Failed to locate Messages resource.");
+ messages_load(path);
+
+ /* Totally pedantic, but base the taskname on the build options.
+ */
+ error = xwimp_initialise(wimp_VERSION_RO38, "NSTheme",
+ (const wimp_message_list *) &task_messages, 0,
+ &task_handle);
+ if (error) {
+ LOG(("xwimp_initialise failed: 0x%x: %s",
+ error->errnum, error->errmess));
+ die(error->errmess);
+ }
+
+ /* Open the templates
+ */
+ if ((length = snprintf(path, sizeof(path),
+ "<NSTheme$Dir>.Resources.%s.Templates",
+ option_language)) < 0 || length >= (int)sizeof(path))
+ die("Failed to locate Templates resource.");
+ error = xwimp_open_template(path);
+ if (error) {
+ LOG(("xwimp_open_template failed: 0x%x: %s",
+ error->errnum, error->errmess));
+ die(error->errmess);
+ }
+ ro_gui_dialog_init();
+ ro_gui_menus_init();
+ xwimp_close_template();
+
+ ro_gui_icon_bar_create();
+}
+
+/**
+ * Determine the language to use.
+ *
+ * RISC OS has no standard way of determining which language the user prefers.
+ * We have to guess from the 'Country' setting.
+ */
+
+void ro_gui_choose_language(void)
+{
+ char path[40];
+ const char *lang;
+ int country;
+ os_error *error;
+
+ /* if option_language exists and is valid, use that */
+ if (option_language) {
+ if (2 < strlen(option_language))
+ option_language[2] = 0;
+ sprintf(path, "<NSTheme$Dir>.Resources.%s", option_language);
+ if (is_dir(path)) return;
+ free(option_language);
+ option_language = 0;
+ }
+
+ /* choose a language from the configured country number */
+ error = xosbyte_read(osbyte_VAR_COUNTRY_NUMBER, &country);
+ if (error) {
+ LOG(("xosbyte_read failed: 0x%x: %s",
+ error->errnum, error->errmess));
+ country = 1;
+ }
+ switch (country) {
+ case 6: /* France */
+ case 18: /* Canada2 (French Canada?) */
+ lang = "fr";
+ break;
+ default:
+ lang = "en";
+ break;
+ }
+ sprintf(path, "<NSTheme$Dir>.Resources.%s", lang);
+ if (is_dir(path))
+ option_language = strdup(lang);
+ else
+ option_language = strdup("en");
+ assert(option_language);
+}
+
+
+/**
+ * Create an iconbar icon.
+ */
+
+void ro_gui_icon_bar_create(void)
+{
+ wimp_icon_create icon = {
+ wimp_ICON_BAR_RIGHT,
+ { { 0, 0, 68, 68 },
+ wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
+ (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT),
+ { "!nstheme" } } };
+ wimp_create_icon(&icon);
+}
+
+
+
+/**
+ * Close down the gui (RISC OS).
+ */
+
+void gui_exit(void) {
+ xosfile_create_dir("<Choices$Write>.NSTheme", 0);
+ options_write("<Choices$Write>.NSTheme.Choices");
+ xwimp_close_down(task_handle);
+ xhourglass_off();
+}
+
+
+/**
+ * Poll the OS for events (RISC OS).
+ *
+ * \param active return as soon as possible
+ */
+
+void gui_poll(void)
+{
+ wimp_event_no event;
+ wimp_block block;
+ const wimp_poll_flags mask = wimp_MASK_LOSE | wimp_MASK_GAIN;
+
+ /* Process queued events. */
+ while (ro_gui_poll_queued_blocks) {
+ struct ro_gui_poll_block *next;
+ ro_gui_handle_event(ro_gui_poll_queued_blocks->event,
+ ro_gui_poll_queued_blocks->block);
+ next = ro_gui_poll_queued_blocks->next;
+ free(ro_gui_poll_queued_blocks->block);
+ free(ro_gui_poll_queued_blocks);
+ ro_gui_poll_queued_blocks = next;
+ }
+
+ /* Poll wimp. */
+ xhourglass_off();
+ event = wimp_poll(wimp_MASK_NULL | mask, &block, 0);
+ xhourglass_on();
+ gui_last_poll = clock();
+ ro_gui_handle_event(event, &block);
+}
+
+
+/**
+ * Process a Wimp_Poll event.
+ *
+ * \param event wimp event number
+ * \param block parameter block
+ */
+
+void ro_gui_handle_event(wimp_event_no event, wimp_block *block)
+{
+ switch (event) {
+ case wimp_OPEN_WINDOW_REQUEST:
+ ro_gui_open_window_request(&block->open);
+ break;
+
+ case wimp_CLOSE_WINDOW_REQUEST:
+ ro_gui_close_window_request(&block->close);
+ break;
+
+ case wimp_MOUSE_CLICK:
+ ro_gui_mouse_click(&block->pointer);
+ break;
+
+ case wimp_USER_DRAG_BOX:
+ ro_gui_drag_end(&(block->dragged));
+ break;
+
+ case wimp_KEY_PRESSED:
+ ro_gui_keypress(&(block->key));
+ break;
+
+ case wimp_MENU_SELECTION:
+ ro_gui_menu_selection(&(block->selection));
+ break;
+
+ case wimp_USER_MESSAGE:
+ case wimp_USER_MESSAGE_RECORDED:
+ case wimp_USER_MESSAGE_ACKNOWLEDGE:
+ ro_gui_user_message(event, &(block->message));
+ break;
+ }
+}
+
+
+/**
+ * Check for important events and yield CPU (RISC OS).
+ *
+ * Required on RISC OS for cooperative multitasking.
+ */
+
+void gui_multitask(void)
+{
+ wimp_event_no event;
+ wimp_block block;
+
+ if (clock() < gui_last_poll + 10)
+ return;
+
+ xhourglass_off();
+ event = wimp_poll(wimp_MASK_LOSE | wimp_MASK_GAIN, &block, 0);
+ xhourglass_on();
+ gui_last_poll = clock();
+
+ switch (event) {
+ case wimp_CLOSE_WINDOW_REQUEST:
+ /* \todo close the window, and destroy content
+ * or abort loading of content */
+ break;
+
+ case wimp_KEY_PRESSED:
+ case wimp_MENU_SELECTION:
+ ro_gui_poll_queue(event, &block);
+ break;
+
+ default:
+ ro_gui_handle_event(event, &block);
+ break;
+ }
+}
+
+
+/**
+ * Add a wimp_block to the queue for later handling.
+ */
+
+void ro_gui_poll_queue(wimp_event_no event, wimp_block *block)
+{
+ struct ro_gui_poll_block *q =
+ calloc(1, sizeof(struct ro_gui_poll_block));
+ if (!q) return;
+ q->event = event;
+ q->block = calloc(1, sizeof(*block));
+ if (!q->block) {
+ free(q);
+ return;
+ }
+ memcpy(q->block, block, sizeof(*block));
+ q->next = NULL;
+
+ if (ro_gui_poll_queued_blocks == NULL) {
+ ro_gui_poll_queued_blocks = q;
+ return;
+ } else {
+ struct ro_gui_poll_block *current =
+ ro_gui_poll_queued_blocks;
+ while (current->next != NULL)
+ current = current->next;
+ current->next = q;
+ }
+ return;
+}
+
+
+/**
+ * Handle Open_Window_Request events.
+ */
+
+void ro_gui_open_window_request(wimp_open *open) {
+ os_error *error;
+ error = xwimp_open_window(open);
+ if (error) {
+ LOG(("xwimp_open_window: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Handle Close_Window_Request events.
+ */
+
+void ro_gui_close_window_request(wimp_close *close) {
+ ro_gui_dialog_close(close->w);
+}
+
+
+/**
+ * Handle Mouse_Click events.
+ */
+void ro_gui_mouse_click(wimp_pointer *pointer) {
+ if (pointer->w == wimp_ICON_BAR) {
+ ro_gui_icon_bar_click(pointer);
+ } else if (pointer->w == dialog_saveas) {
+ ro_gui_save_click(pointer);
+ } else {
+ ro_gui_dialog_click(pointer);
+ }
+}
+
+/**
+ * Handle Mouse_Click events on the iconbar icon.
+ */
+
+void ro_gui_icon_bar_click(wimp_pointer *pointer)
+{
+ if (pointer->buttons == wimp_CLICK_MENU) {
+ ro_gui_create_menu(iconbar_menu, pointer->pos.x,
+ 96 + iconbar_menu_height);
+ } else if (pointer->buttons == wimp_CLICK_SELECT) {
+ ro_gui_dialog_prepare_main();
+ ro_gui_open_window_centre(NULL, dialog_main);
+ ro_gui_set_caret_first(dialog_main);
+ }
+}
+
+
+/**
+ * Handle User_Drag_Box events.
+ */
+
+void ro_gui_drag_end(wimp_dragged *drag) {
+ ro_gui_save_drag_end(drag);
+}
+
+
+/**
+ * Handle Key_Pressed events.
+ */
+
+void ro_gui_keypress(wimp_key *key) {
+ bool handled = false;
+ os_error *error;
+
+ if (key->c == wimp_KEY_F1) {
+ xos_cli("Filer_Run <NSTheme$Dir>.!Help");
+ }
+
+ if (key->w == dialog_saveas) {
+// handled = ro_gui_saveas_keypress(key->c);
+ } else {
+ handled = ro_gui_dialog_keypress(key);
+ }
+
+ if (!handled) {
+ error = xwimp_process_key(key->c);
+ if (error) {
+ LOG(("xwimp_process_key: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ }
+ }
+}
+
+
+/**
+ * Handle the three User_Message events.
+ */
+
+void ro_gui_user_message(wimp_event_no event, wimp_message *message)
+{
+ switch (message->action) {
+ case message_HELP_REQUEST:
+ ro_gui_interactive_help_request(message);
+ break;
+
+ case message_DATA_SAVE_ACK:
+ ro_msg_datasave_ack(message);
+ break;
+
+ case message_DATA_LOAD:
+ if (event == wimp_USER_MESSAGE_ACKNOWLEDGE) {
+ }
+ else
+ ro_msg_dataload(message);
+ break;
+
+ case message_DATA_LOAD_ACK:
+ break;
+
+ case message_MENU_WARNING:
+ ro_gui_menu_warning((wimp_message_menu_warning *)
+ &message->data);
+ break;
+
+ case message_QUIT:
+ application_quit = true;
+ break;
+ }
+}
+
+
+/**
+ * Handle Message_DataLoad (file dragged in).
+ */
+
+void ro_msg_dataload(wimp_message *message) {
+ os_error *error;
+ osspriteop_area *area;
+ bool success = false;
+ char *filename = message->data.data_xfer.file_name;
+ int file_type = message->data.data_xfer.file_type;
+ if ((file_type == 0xff9) && (message->data.data_xfer.w == dialog_main)) {
+ area = ro_gui_load_sprite_file(filename);
+ if (area) {
+ success = true;
+ if (theme_sprites) free(theme_sprites);
+ theme_sprites = area;
+ ro_gui_dialog_prepare_main();
+ if (sprite_filename) {
+ free(sprite_filename);
+ sprite_filename = NULL;
+ }
+ }
+ } else {
+ success = ro_gui_load_theme(filename);
+ if (success) {
+ ro_gui_dialog_prepare_main();
+ ro_gui_open_window_centre(NULL, dialog_main);
+ ro_gui_set_caret_first(dialog_main);
+ }
+ }
+
+ if (!success) return;
+ message->action = message_DATA_LOAD_ACK;
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
+ if (error) {
+ LOG(("xwimp_send_message: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Handle Message_DataSaveAck.
+ */
+
+void ro_msg_datasave_ack(wimp_message *message) {
+ ro_gui_save_datasave_ack(message);
+}
+
+
+/**
+ * Find screen size in OS units.
+ */
+
+void ro_gui_screen_size(int *width, int *height)
+{
+ int xeig_factor, yeig_factor, xwind_limit, ywind_limit;
+
+ os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR, &xeig_factor);
+ os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR, &yeig_factor);
+ os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT, &xwind_limit);
+ os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT, &ywind_limit);
+ *width = (xwind_limit + 1) << xeig_factor;
+ *height = (ywind_limit + 1) << yeig_factor;
+}
+
+
+/**
+ * Display a warning for a serious problem (eg memory exhaustion).
+ *
+ * \param warning message key for warning message
+ * \param detail additional message, or 0
+ */
+
+void warn_user(const char *warning, const char *detail)
+{
+ static char warn_buffer[300];
+
+ LOG(("%s %s", warning, detail));
+ snprintf(warn_buffer, sizeof warn_buffer, "%s %s",
+ messages_get(warning),
+ detail ? detail : "");
+ warn_buffer[sizeof warn_buffer - 1] = 0;
+ ro_gui_set_icon_string(dialog_warning, ICON_WARNING_MESSAGE,
+ warn_buffer);
+ xwimp_set_icon_state(dialog_warning, ICON_WARNING_HELP,
+ wimp_ICON_DELETED, wimp_ICON_DELETED);
+ ro_gui_dialog_open(dialog_warning);
+ xos_bell();
+}
+
+
+/**
+ * Display an error and exit.
+ *
+ * Should only be used during initialisation.
+ */
+
+void die(const char *error)
+{
+ os_error warn_error;
+
+ warn_error.errnum = 1; /* \todo: reasonable ? */
+ strncpy(warn_error.errmess, messages_get(error),
+ sizeof(warn_error.errmess)-1);
+ warn_error.errmess[sizeof(warn_error.errmess)-1] = '\0';
+ xwimp_report_error_by_category(&warn_error,
+ wimp_ERROR_BOX_OK_ICON |
+ wimp_ERROR_BOX_GIVEN_CATEGORY |
+ wimp_ERROR_BOX_CATEGORY_ERROR <<
+ wimp_ERROR_BOX_CATEGORY_SHIFT,
+ "NSTheme", "!nstheme",
+ (osspriteop_area *) 1, 0, 0);
+ exit(EXIT_FAILURE);
+}
diff --git a/riscos/gui.h b/riscos/gui.h
new file mode 100644
index 0000000..54b312c
--- /dev/null
+++ b/riscos/gui.h
@@ -0,0 +1,97 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
+ */
+
+#ifndef _NETSURF_RISCOS_GUI_H_
+#define _NETSURF_RISCOS_GUI_H_
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include "oslib/osspriteop.h"
+#include "oslib/wimp.h"
+#include "nstheme/desktop/nstheme.h"
+#include "nstheme/desktop/gui.h"
+#include "nstheme/desktop/options.h"
+
+#define THEMES_DIR "<NSTheme$Dir>.Themes"
+
+extern char *theme_filename;
+extern char *sprite_filename;
+extern osspriteop_area *theme_sprites;
+extern wimp_w dialog_info, dialog_main, dialog_saveas, dialog_warning;
+extern wimp_menu *iconbar_menu, *main_menu, *colour_menu;
+extern int iconbar_menu_height;
+extern wimp_menu *current_menu;
+
+struct theme_file_header {
+ unsigned int magic_value;
+ unsigned int parser_version;
+ char name[32];
+ char author[64];
+ char browser_bg;
+ char hotlist_bg;
+ char status_bg;
+ char status_fg;
+ char throbber_left;
+ char future_expansion_1;
+ char future_expansion_2;
+ char future_expansion_3;
+ unsigned int compressed_sprite_size;
+ unsigned int decompressed_sprite_size;
+};
+
+/* in gui.c */
+void ro_gui_screen_size(int *width, int *height);
+//void ro_gui_drag_box_start(wimp_pointer *pointer);
+
+/* in menus.c */
+void ro_gui_menus_init(void);
+void ro_gui_create_menu(wimp_menu* menu, int x, int y);
+void ro_gui_popup_menu(wimp_menu *menu, wimp_w w, wimp_i i);
+void ro_gui_menu_selection(wimp_selection* selection);
+void ro_gui_menu_warning(wimp_message_menu_warning *warning);
+
+/* in dialog.c */
+void ro_gui_dialog_init(void);
+void ro_gui_dialog_open(wimp_w w);
+void ro_gui_dialog_close(wimp_w close);
+void ro_gui_dialog_click(wimp_pointer *pointer);
+bool ro_gui_dialog_keypress(wimp_key *key);
+void ro_gui_dialog_prepare_main(void);
+
+/* in save.c */
+void ro_gui_save_open(int save_type, int x, int y);
+void ro_gui_save_click(wimp_pointer *pointer);
+void ro_gui_save_drag_end(wimp_dragged *drag);
+void ro_gui_save_datasave_ack(wimp_message *message);
+bool ro_gui_load_theme(char *path);
+
+#define ICON_WARNING_MESSAGE 0
+#define ICON_WARNING_CONTINUE 1
+#define ICON_WARNING_HELP 2
+
+#define ICON_MAIN_NAME 3
+#define ICON_MAIN_AUTHOR 5
+#define ICON_MAIN_THROBBER_LEFT 8
+#define ICON_MAIN_REPORT 11
+#define ICON_MAIN_REMOVE 12
+#define ICON_MAIN_BROWSER_COLOUR 16
+#define ICON_MAIN_BROWSER_MENU 17
+#define ICON_MAIN_HOTLIST_COLOUR 19
+#define ICON_MAIN_HOTLIST_MENU 20
+#define ICON_MAIN_STATUSBG_COLOUR 22
+#define ICON_MAIN_STATUSBG_MENU 23
+#define ICON_MAIN_STATUSFG_COLOUR 25
+#define ICON_MAIN_STATUSFG_MENU 26
+
+#define ICON_SAVE_ICON 0
+#define ICON_SAVE_PATH 1
+#define ICON_SAVE_OK 2
+#define ICON_SAVE_CANCEL 3
+
+#endif
diff --git a/riscos/help.c b/riscos/help.c
new file mode 100644
index 0000000..62bfd8a
--- /dev/null
+++ b/riscos/help.c
@@ -0,0 +1,257 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * Interactive help (implementation).
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/help.h"
+#include "oslib/os.h"
+#include "oslib/taskmanager.h"
+#include "oslib/wimp.h"
+#include "nstheme/riscos/gui.h"
+#include "nstheme/riscos/help.h"
+#include "nstheme/riscos/wimp.h"
+#include "nstheme/utils/messages.h"
+#include "nstheme/utils/log.h"
+
+
+/* Recognised help keys
+ ====================
+
+ HelpIconbar Iconbar (no icon suffix is used)
+
+ HelpAppInfo Application info window
+ HelpMain Main application window
+ HelpSaveAs Save as window
+ HelpWarning Warning window
+
+ HelpIconMenu Iconbar menu
+ HelpMainMenu Main application menu
+
+ The prefixes are followed by either the icon number (eg 'HelpToolbar7'), or a series
+ of numbers representing the menu structure (eg 'HelpBrowserMenu3-1-2').
+ If '<key><identifier>' is not available, then simply '<key>' is then used. For example
+ if 'HelpToolbar7' is not available then 'HelpToolbar' is then tried.
+
+ If an item is greyed out then a suffix of 'g' is added (eg 'HelpToolbar7g'). For this to
+ work, windows must have bit 4 of the window flag byte set and the user must be running
+ RISC OS 5.03 or greater.
+*/
+
+static void ro_gui_interactive_help_broadcast(wimp_message *message, char *token);
+static os_t help_time = 0;
+
+/**
+ * Attempts to process an interactive help message request
+ *
+ * \param message the request message
+ */
+void ro_gui_interactive_help_request(wimp_message *message) {
+ char message_token[32];
+ char menu_buffer[4];
+ wimp_selection menu_tree;
+ help_full_message_request *message_data;
+ wimp_w window;
+ wimp_i icon;
+ unsigned int index;
+ bool greyed = false;
+ wimp_menu *test_menu;
+
+ /* Ensure we have a help request
+ */
+ if ((!message) || (message->action != message_HELP_REQUEST)) return;
+
+ /* Remember the time of the request
+ */
+ xos_read_monotonic_time(&help_time);
+
+ /* Initialise the basic token to a null byte
+ */
+ message_token[0] = 0x00;
+
+ /* Get the message data
+ */
+ message_data = (help_full_message_request *)message;
+ window = message_data->w;
+ icon = message_data->i;
+
+ /* Do the basic window checks
+ */
+ if (window == (wimp_w)-2) {
+ sprintf(message_token, "HelpIconbar");
+ } else if (window == dialog_info) {
+ sprintf(message_token, "HelpAppInfo%i", (int)icon);
+ } else if (window == dialog_main) {
+ sprintf(message_token, "HelpMain%i", (int)icon);
+ } else if (window == dialog_saveas) {
+ sprintf(message_token, "HelpSaveAs%i", (int)icon);
+ } else if (window == dialog_warning) {
+ sprintf(message_token, "HelpWarning%i", (int)icon);
+ }
+
+ /* If we've managed to find something so far then we broadcast it
+ */
+ if (message_token[0] != 0x00) {
+ /* Check to see if we are greyed out
+ */
+ if ((icon >= 0) && (ro_gui_get_icon_shaded_state(window, icon))) {
+ strcat(message_token, "g");
+ }
+
+ /* Broadcast out message
+ */
+ ro_gui_interactive_help_broadcast(message, &message_token[0]);
+ return;
+ }
+
+ /* If we are not on an icon, we can't be in a menu (which stops separators
+ giving help for their parent) so we abort. You don't even want to think
+ about the awful hack I was considering before I realised this...
+ */
+ if (icon == (wimp_i)-1) return;
+
+ /* As a last resort, check for menu help.
+ */
+ if (xwimp_get_menu_state((wimp_menu_state_flags)1,
+ &menu_tree,
+ window, icon)) return;
+ if (menu_tree.items[0] == -1) return;
+
+ /* Set the prefix
+ */
+ if (current_menu == iconbar_menu) {
+ sprintf(message_token, "HelpIconMenu");
+ } else if (current_menu == main_menu) {
+ sprintf(message_token, "HelpMainMenu");
+ } else {
+ return;
+ }
+
+ /* Decode the menu
+ */
+ index = 0;
+ test_menu = current_menu;
+ while (menu_tree.items[index] != -1) {
+ /* Check if we're greyed out
+ */
+ greyed |= test_menu->entries[menu_tree.items[index]].icon_flags & wimp_ICON_SHADED;
+ test_menu = test_menu->entries[menu_tree.items[index]].sub_menu;
+
+ /* Continue adding the entries
+ */
+ if (index == 0) {
+ sprintf(menu_buffer, "%i", menu_tree.items[index]);
+ } else {
+ sprintf(menu_buffer, "-%i", menu_tree.items[index]);
+ }
+ strcat(message_token, menu_buffer);
+ index++;
+ }
+ if (greyed) strcat(message_token, "g");
+
+ /* Finally, broadcast the menu help
+ */
+ ro_gui_interactive_help_broadcast(message, &message_token[0]);
+}
+
+
+/**
+ * Broadcasts a help reply
+ *
+ * \param message the original request message
+ * \param token the token to look up
+ */
+static void ro_gui_interactive_help_broadcast(wimp_message *message, char *token) {
+ const char *translated_token;
+ help_full_message_reply *reply;
+ char *base_token;
+
+ /* Start off with an empty reply
+ */
+ reply = (help_full_message_reply *)message;
+ reply->reply[0] = '\0';
+
+ /* Check if the message exists
+ */
+ translated_token = messages_get(token);
+ if (translated_token == token) {
+ /* We must never provide default help for a 'g' suffix.
+ */
+ if (token[strlen(token) - 1] != 'g') {
+ /* Find the key from the token.
+ */
+ base_token = token;
+ while (base_token[0] != 0x00) {
+ if ((base_token[0] == '-') ||
+ ((base_token[0] >= '0') && (base_token[0] <= '9'))) {
+ base_token[0] = 0x00;
+ } else {
+ ++base_token;
+ }
+ }
+
+ /* Check if the base key exists and use an empty string if not
+ */
+ translated_token = messages_get(token);
+ }
+ }
+
+
+ /* Copy our message string
+ */
+ if (translated_token != token) {
+ reply->reply[235] = 0;
+ strncpy(reply->reply, translated_token, 235);
+ }
+
+ /* Broadcast the help reply
+ */
+ reply->size = 256;
+ reply->action = message_HELP_REPLY;
+ reply->your_ref = reply->my_ref;
+ wimp_send_message(wimp_USER_MESSAGE, (wimp_message *)reply, reply->sender);
+}
+
+
+/**
+ * Checks if interactive help is running
+ *
+ * \return non-zero if interactive help is available, or 0 if not available
+ */
+int ro_gui_interactive_help_available() {
+ taskmanager_task task;
+ int context = 0;
+ char *end;
+ os_t time;
+
+ /* Check if we've received a help request in the last 0.5s to test for generic
+ interactive help applications
+ */
+ xos_read_monotonic_time(&time);
+ if ((help_time + 50) > time) return true;
+
+ /* Attempt to find the task 'Help'
+ */
+ do {
+ if (xtaskmanager_enumerate_tasks(context, &task, sizeof(taskmanager_task),
+ &context, &end)) return 0;
+
+ /* We can't just use strcmp due to string termination issues.
+ */
+ if (strncmp(task.name, "Help", 4) == 0) {
+ if (task.name[4] < 32) return true;
+ }
+ } while (context >= 0);
+
+ /* Return failure
+ */
+ return 0;
+}
diff --git a/riscos/help.h b/riscos/help.h
new file mode 100644
index 0000000..444dec7
--- /dev/null
+++ b/riscos/help.h
@@ -0,0 +1,20 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * Interactive help (interface).
+ */
+
+#ifndef _NETSURF_RISCOS_HELP_H_
+#define _NETSURF_RISCOS_HELP_H_
+
+#include "oslib/wimp.h"
+
+void ro_gui_interactive_help_request(wimp_message *message);
+int ro_gui_interactive_help_available(void);
+
+#endif
diff --git a/riscos/menus.c b/riscos/menus.c
new file mode 100644
index 0000000..f1711a3
--- /dev/null
+++ b/riscos/menus.c
@@ -0,0 +1,272 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * Menu creation and handling (implementation).
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/osgbpb.h"
+#include "oslib/wimp.h"
+#include "nstheme/desktop/gui.h"
+#include "nstheme/riscos/gui.h"
+#include "nstheme/riscos/help.h"
+#include "nstheme/riscos/options.h"
+#include "nstheme/riscos/wimp.h"
+#include "nstheme/utils/log.h"
+#include "nstheme/utils/messages.h"
+#include "nstheme/utils/utils.h"
+
+
+static void translate_menu(wimp_menu *menu);
+
+
+wimp_menu *current_menu;
+wimp_i menu_icon;
+
+/* Default menu item flags
+*/
+#define COLOUR_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT))
+#define DEFAULT_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \
+ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | \
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT))
+
+
+/* Iconbar menu
+*/
+static wimp_MENU(4) ibar_menu = {
+ { "NSTheme" }, 7,2,7,0, 200, 44, 0,
+ {
+ { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Info" } },
+ { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "AppHelp" } },
+ { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Quit" } }
+ }
+};
+int iconbar_menu_height = 3 * 44;
+wimp_menu *iconbar_menu = (wimp_menu *)&ibar_menu;
+
+
+/* Colour menu
+*/
+static wimp_MENU(16) col_menu = {
+ { "Colour" }, 7,2,7,0, 200, 44, 0,
+ {
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_MID_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_MID_DARK_GREY << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_DARK_GREY << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_VERY_DARK_GREY << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_BLACK << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_DARK_BLUE << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_YELLOW << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_LIGHT_GREEN << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_RED << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_CREAM << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_DARK_GREEN << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { 0, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_ORANGE << wimp_ICON_BG_COLOUR_SHIFT), { "" } },
+ { wimp_MENU_LAST, wimp_NO_SUB_MENU, COLOUR_FLAGS |
+ (wimp_COLOUR_LIGHT_BLUE << wimp_ICON_BG_COLOUR_SHIFT), { "" } }
+ }
+};
+wimp_menu *colour_menu = (wimp_menu *)&col_menu;
+
+/* Main browser menu
+*/
+static wimp_MENU(3) menu = {
+ { "NSTheme" }, 7,2,7,0, 200, 44, 0,
+ {
+ { wimp_MENU_GIVE_WARNING, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "SaveAs" } },
+ { wimp_MENU_GIVE_WARNING, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Export" } },
+ { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "AppHelp" } }
+ }
+};
+wimp_menu *main_menu = (wimp_menu *) &menu;
+
+
+/**
+ * Create menu structures.
+ */
+
+void ro_gui_menus_init(void)
+{
+ translate_menu(iconbar_menu);
+ translate_menu(main_menu);
+ translate_menu(colour_menu);
+
+ iconbar_menu->entries[0].sub_menu = (wimp_menu *)dialog_info;
+ main_menu->entries[0].sub_menu = (wimp_menu *)dialog_saveas;
+ main_menu->entries[1].sub_menu = (wimp_menu *)dialog_saveas;
+}
+
+
+/**
+ * Replace text in a menu with message values.
+ */
+
+void translate_menu(wimp_menu *menu)
+{
+ unsigned int i = 0;
+ const char *indirected_text;
+
+ /* We can't just blindly set something as indirected as if we use
+ the fallback messages text (ie the pointer we gave), we overwrite
+ this data when setting the pointer to the indirected text we
+ already had.
+ */
+ indirected_text = messages_get(menu->title_data.text);
+ if (indirected_text != menu->title_data.text) {
+ menu->title_data.indirected_text.text = indirected_text;
+ menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
+
+ }
+
+ /* items */
+ do {
+ indirected_text = messages_get(menu->entries[i].data.text);
+ if (indirected_text != menu->entries[i].data.text) {
+ menu->entries[i].icon_flags |= wimp_ICON_INDIRECTED;
+ menu->entries[i].data.indirected_text.text = indirected_text;
+ menu->entries[i].data.indirected_text.validation = 0;
+ menu->entries[i].data.indirected_text.size = strlen(indirected_text) + 1;
+ }
+ i++;
+ } while ((menu->entries[i - 1].menu_flags & wimp_MENU_LAST) == 0);
+}
+
+
+/**
+ * Display a menu.
+ */
+
+void ro_gui_create_menu(wimp_menu *menu, int x, int y) {
+ os_error *error;
+ current_menu = menu;
+ int colour;
+
+ if (menu == colour_menu) {
+ colour = ro_gui_get_icon_background_colour(dialog_main, menu_icon - 1);
+ for (int i = 0; i < 16; i++) {
+ if (i == colour) {
+ colour_menu->entries[i].menu_flags |= wimp_MENU_TICKED;
+ } else {
+ colour_menu->entries[i].menu_flags &= ~wimp_MENU_TICKED;
+ }
+ }
+ } else if (menu == main_menu) {
+ if (theme_sprites) {
+ main_menu->entries[1].icon_flags &= ~wimp_ICON_SHADED;
+ } else {
+ main_menu->entries[1].icon_flags |= wimp_ICON_SHADED;
+ }
+ }
+
+ error = xwimp_create_menu(menu, x - 64, y);
+ if (error) {
+ LOG(("xwimp_create_menu: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MenuError", error->errmess);
+ }
+}
+
+
+/**
+ * Display a pop-up menu next to the specified icon.
+ */
+
+void ro_gui_popup_menu(wimp_menu *menu, wimp_w w, wimp_i i) {
+ wimp_window_state state;
+ wimp_icon_state icon_state;
+ state.w = w;
+ icon_state.w = w;
+ icon_state.i = i;
+ wimp_get_window_state(&state);
+ wimp_get_icon_state(&icon_state);
+ menu_icon = i;
+ ro_gui_create_menu(menu,
+ state.visible.x0 + icon_state.icon.extent.x1 + 64,
+ state.visible.y1 + icon_state.icon.extent.y1);
+}
+
+
+/**
+ * Handle menu selection.
+ */
+
+void ro_gui_menu_selection(wimp_selection *selection) {
+ wimp_pointer pointer;
+
+ wimp_get_pointer_info(&pointer);
+
+ if (current_menu == iconbar_menu) {
+ switch (selection->items[0]) {
+ case 0: /* Info */
+ ro_gui_create_menu((wimp_menu *) dialog_info,
+ pointer.pos.x, pointer.pos.y);
+ break;
+ case 1: /* Help */
+ xos_cli("Filer_Run <NSTheme$Dir>.!Help");
+ break;
+ case 2: /* Quit */
+ application_quit = true;
+ break;
+ }
+
+ } else if (current_menu == main_menu) {
+ switch (selection->items[0]) {
+ case 2: /* Help */
+ xos_cli("Filer_Run <NSTheme$Dir>.!Help");
+ break;
+ }
+ } else if (current_menu == colour_menu) {
+ ro_gui_set_icon_background_colour(dialog_main, menu_icon - 1,
+ selection->items[0]);
+ }
+
+ if (pointer.buttons == wimp_CLICK_ADJUST) {
+ ro_gui_create_menu(current_menu, 0, 0);
+ }
+
+}
+
+/**
+ * Handle Message_MenuWarning.
+ */
+
+void ro_gui_menu_warning(wimp_message_menu_warning *warning) {
+ if (current_menu != main_menu) return;
+ switch (warning->selection.items[0]) {
+ case 0:
+ ro_gui_save_open(1, warning->pos.x, warning->pos.y);
+ break;
+ case 1:
+ ro_gui_save_open(2, warning->pos.x, warning->pos.y);
+ break;
+ }
+}
diff --git a/riscos/options.h b/riscos/options.h
new file mode 100644
index 0000000..b003da1
--- /dev/null
+++ b/riscos/options.h
@@ -0,0 +1,24 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * RISC OS specific options.
+ */
+
+#ifndef _NETSURF_RISCOS_OPTIONS_H_
+#define _NETSURF_RISCOS_OPTIONS_H_
+
+#include "netsurf/desktop/options.h"
+
+extern char *option_language;
+
+#define EXTRA_OPTION_DEFINE \
+char *option_language = 0;\
+
+#define EXTRA_OPTION_TABLE \
+{ "language", OPTION_STRING, &option_language }
+#endif
diff --git a/riscos/save.c b/riscos/save.c
new file mode 100644
index 0000000..c6bc06f
--- /dev/null
+++ b/riscos/save.c
@@ -0,0 +1,554 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ */
+
+/** \file
+ * Save dialog and drag and drop saving (implementation).
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "oslib/dragasprite.h"
+#include "oslib/osgbpb.h"
+#include "oslib/osfile.h"
+#include "oslib/osfind.h"
+#include "oslib/osspriteop.h"
+#include "oslib/squash.h"
+#include "oslib/wimp.h"
+#include "nstheme/riscos/gui.h"
+#include "nstheme/riscos/wimp.h"
+#include "nstheme/utils/log.h"
+#include "nstheme/utils/messages.h"
+#include "nstheme/utils/utils.h"
+
+#define SAVE_THEME 1
+#define SAVE_SPRITES 2
+static int gui_save_current_type = 0;
+static char *reset_filename;
+static unsigned int gui_save_filetype;
+
+char *theme_filename = NULL;
+char *sprite_filename = NULL;
+static bool close_menu = true;
+
+static void ro_gui_drag_icon(wimp_pointer *pointer);
+static bool ro_gui_save(char *path);
+static bool ro_gui_save_theme(char *path);
+static bool ro_gui_save_sprites(char *path);
+
+/**
+ * Prepares the save box to reflect gui_save_type and a content, and
+ * opens it.
+ *
+ * \param save_type type of save
+ * \param c content to save
+ * \param sub_menu open dialog as a sub menu, otherwise persistent
+ * \param x x position, for sub_menu true only
+ * \param y y position, for sub_menu true only
+ * \param parent parent window for persistent box, for sub_menu false only
+ */
+
+void ro_gui_save_open(int save_type, int x, int y)
+{
+ char icon_buf[20];
+ const char *icon = icon_buf;
+ os_error *error;
+
+ gui_save_current_type = save_type;
+ if (save_type == SAVE_THEME) {
+ if (theme_filename == NULL) {
+ theme_filename = malloc(6);
+ if (!theme_filename) {
+ LOG(("No memory for malloc()"));
+ warn_user("NoMemory", 0);
+ return;
+ }
+ sprintf(theme_filename, "Theme");
+ }
+ gui_save_filetype = 0xffd;
+ ro_gui_set_window_title(dialog_saveas, messages_get("SaveTitle"));
+ ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_PATH, theme_filename);
+ reset_filename = theme_filename;
+ } else {
+ if (sprite_filename == NULL) {
+ sprite_filename = malloc(8);
+ if (!sprite_filename) {
+ LOG(("No memory for malloc()"));
+ warn_user("NoMemory", 0);
+ return;
+ }
+ sprintf(sprite_filename, "Sprites");
+ }
+ gui_save_filetype = 0xff9;
+ ro_gui_set_window_title(dialog_saveas, messages_get("ExportTitle"));
+ ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_PATH, sprite_filename);
+ reset_filename = sprite_filename;
+ }
+
+
+ /* icon */
+ sprintf(icon_buf, "file_%.3x", gui_save_filetype);
+ ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_ICON, icon);
+
+ /* open sub menu or persistent dialog */
+ error = xwimp_create_sub_menu((wimp_menu *) dialog_saveas,
+ x, y);
+ if (error) {
+ LOG(("xwimp_create_sub_menu: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MenuError", error->errmess);
+ }
+}
+
+
+/**
+ * Handle clicks in the save dialog.
+ */
+
+void ro_gui_save_click(wimp_pointer *pointer)
+{
+ switch (pointer->i) {
+ case ICON_SAVE_OK:
+ close_menu = (pointer->buttons == wimp_CLICK_SELECT);
+ ro_gui_save(ro_gui_get_icon_string(dialog_saveas, ICON_SAVE_PATH));
+ if (pointer->buttons == wimp_CLICK_SELECT) {
+ xwimp_create_menu((wimp_menu *)-1, 0, 0);
+ ro_gui_dialog_close(pointer->w);
+ }
+ break;
+ case ICON_SAVE_CANCEL:
+ if (pointer->buttons == wimp_CLICK_SELECT) {
+ xwimp_create_menu((wimp_menu *)-1, 0, 0);
+ ro_gui_dialog_close(pointer->w);
+ } else if (pointer->buttons == wimp_CLICK_ADJUST) {
+ ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_PATH, reset_filename);
+ }
+ break;
+ case ICON_SAVE_ICON:
+ if (pointer->buttons == wimp_DRAG_SELECT) {
+ ro_gui_drag_icon(pointer);
+ }
+ break;
+ }
+}
+
+
+/**
+ * Start drag of icon under the pointer.
+ */
+
+void ro_gui_drag_icon(wimp_pointer *pointer)
+{
+ char *sprite;
+ os_box box = { pointer->pos.x - 34, pointer->pos.y - 34,
+ pointer->pos.x + 34, pointer->pos.y + 34 };
+ os_error *error;
+
+ if (pointer->i == -1)
+ return;
+
+ sprite = ro_gui_get_icon_string(pointer->w, pointer->i);
+
+ error = xdragasprite_start(dragasprite_HPOS_CENTRE |
+ dragasprite_VPOS_CENTRE |
+ dragasprite_BOUND_POINTER |
+ dragasprite_DROP_SHADOW,
+ (osspriteop_area *) 1, sprite, &box, 0);
+ if (error) {
+ LOG(("xdragasprite_start: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("DragError", error->errmess);
+ }
+}
+
+
+/**
+ * Handle User_Drag_Box event for a drag from the save dialog.
+ */
+
+void ro_gui_save_drag_end(wimp_dragged *drag)
+{
+ char *name;
+ char *dot;
+ wimp_pointer pointer;
+ wimp_message message;
+
+ wimp_get_pointer_info(&pointer);
+
+ name = ro_gui_get_icon_string(dialog_saveas, ICON_SAVE_PATH);
+ dot = strrchr(name, '.');
+ if (dot)
+ name = dot + 1;
+
+ message.your_ref = 0;
+ message.action = message_DATA_SAVE;
+ message.data.data_xfer.w = pointer.w;
+ message.data.data_xfer.i = pointer.i;
+ message.data.data_xfer.pos.x = pointer.pos.x;
+ message.data.data_xfer.pos.y = pointer.pos.y;
+ message.data.data_xfer.est_size = 1000;
+ message.data.data_xfer.file_type = gui_save_filetype;
+ strncpy(message.data.data_xfer.file_name, name, 212);
+ message.data.data_xfer.file_name[211] = 0;
+ message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) &
+ (~3u));
+
+ wimp_send_message_to_window(wimp_USER_MESSAGE, &message,
+ pointer.w, pointer.i);
+}
+
+
+/**
+ * Handle Message_DataSaveAck for a drag from the save dialog.
+ */
+
+void ro_gui_save_datasave_ack(wimp_message *message)
+{
+ char *path = message->data.data_xfer.file_name;
+ os_error *error;
+
+ if (!ro_gui_save(path)) return;
+ ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_PATH, reset_filename);
+
+ /* Close the save window
+ */
+ ro_gui_dialog_close(dialog_saveas);
+
+ /* Ack successful save with message_DATA_LOAD */
+ message->action = message_DATA_LOAD;
+ message->your_ref = message->my_ref;
+ error = xwimp_send_message_to_window(wimp_USER_MESSAGE, message,
+ message->data.data_xfer.w, message->data.data_xfer.i, 0);
+ if (error) {
+ LOG(("xwimp_send_message_to_window: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("FileError", error->errmess);
+ }
+
+ if (close_menu) {
+ error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
+ if (error) {
+ LOG(("xwimp_create_menu: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MenuError", error->errmess);
+ }
+ }
+ close_menu = true;
+}
+
+bool ro_gui_save(char *path) {
+ switch (gui_save_current_type) {
+ case SAVE_THEME:
+ return ro_gui_save_theme(path);
+ case SAVE_SPRITES:
+ return ro_gui_save_sprites(path);
+ }
+ return false;
+}
+
+bool ro_gui_load_theme(char *path) {
+ os_error *error;
+ struct theme_file_header *file_header;
+ char *workspace;
+ int workspace_size, file_size, output_left;
+ fileswitch_object_type obj_type;
+ squash_output_status status;
+ os_fw file_handle;
+ char *raw_data;
+ char *compressed;
+ char *decompressed;
+
+ /* Get memory for the header
+ */
+ file_header = (struct theme_file_header *)calloc(1,
+ sizeof (struct theme_file_header));
+ if (!file_header) {
+ LOG(("No memory for calloc()"));
+ warn_user("NoMemory", 0);
+ }
+
+ /* Load the header
+ */
+ error = xosfind_openinw(osfind_NO_PATH, path, 0, &file_handle);
+ if (error) {
+ free(file_header);
+ LOG(("xosfind_openinw: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+ if (file_handle == 0) {
+ free(file_header);
+ LOG(("File not present"));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+ error = xosgbpb_read_atw(file_handle, (char *)file_header,
+ sizeof (struct theme_file_header),
+ 0, &output_left);
+ xosfind_closew(file_handle);
+ if (error) {
+ free(file_header);
+ LOG(("xosbgpb_read_atw: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+ if (output_left > 0) {
+ free(file_header);
+ LOG(("Insufficient data"));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+
+ /* Check the header is OK
+ */
+ if ((file_header->magic_value != 0x4d54534e) ||
+ (file_header->parser_version > 1)) return false;
+
+ /* Try to load the sprite file
+ */
+ if (file_header->decompressed_sprite_size > 0) {
+ error = xosfile_read_stamped_no_path(path,
+ &obj_type, 0, 0, &file_size, 0, 0);
+ if (error) {
+ free(file_header);
+ LOG(("xosfile_read_stamped_no_path: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+ if (obj_type != fileswitch_IS_FILE) {
+ free(file_header);
+ return false;
+ }
+ raw_data = malloc(file_size);
+ if (!raw_data) {
+ free(file_header);
+ LOG(("No memory for malloc()"));
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ error = xosfile_load_stamped_no_path(path, (byte *)raw_data,
+ 0, 0, 0, 0, 0);
+ if (error) {
+ free(raw_data);
+ free(file_header);
+ LOG(("xosfile_load_stamped_no_path: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+
+ /* Decompress the data
+ */
+ decompressed = malloc(file_header->decompressed_sprite_size);
+ if (!decompressed) {
+ free(raw_data);
+ free(file_header);
+ LOG(("No memory for malloc()"));
+ warn_user("NoMemory", 0);
+ }
+ error = xsquash_decompress_return_sizes(-1, &workspace_size, 0);
+ if (error) {
+ free(decompressed);
+ free(raw_data);
+ free(file_header);
+ LOG(("xsquash_decompress_return_sizes: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MiscError", error->errmess);
+ return false;
+ }
+ workspace = malloc(workspace_size);
+ if (!workspace) {
+ free(decompressed);
+ free(raw_data);
+ free(file_header);
+ LOG(("No memory for malloc()"));
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ compressed = raw_data + sizeof(struct theme_file_header);
+ error = xsquash_decompress(0, /*squash_INPUT_ALL_PRESENT,*/
+ workspace,
+ (byte *)compressed, file_header->compressed_sprite_size,
+ (byte *)decompressed, file_header->decompressed_sprite_size,
+ &status, 0, 0, 0, 0);
+ free(workspace);
+ free(raw_data);
+ if (error) {
+ free(decompressed);
+ LOG(("xsquash_decompress: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MiscError", error->errmess);
+ return false;
+ }
+ if (status != 0) {
+ free(decompressed);
+ return false;
+ }
+ if (theme_sprites) free(theme_sprites);
+ theme_sprites = (osspriteop_area *)decompressed;
+ } else {
+ if (theme_sprites) free(theme_sprites);
+ theme_sprites = NULL;
+ }
+
+ /* Set out values
+ */
+ ro_gui_set_icon_string(dialog_main, ICON_MAIN_NAME, file_header->name);
+ ro_gui_set_icon_string(dialog_main, ICON_MAIN_AUTHOR, file_header->author);
+ ro_gui_set_icon_background_colour(dialog_main, ICON_MAIN_BROWSER_COLOUR,
+ file_header->browser_bg);
+ ro_gui_set_icon_background_colour(dialog_main, ICON_MAIN_HOTLIST_COLOUR,
+ file_header->hotlist_bg);
+ ro_gui_set_icon_background_colour(dialog_main, ICON_MAIN_STATUSBG_COLOUR,
+ file_header->status_bg);
+ ro_gui_set_icon_background_colour(dialog_main, ICON_MAIN_STATUSFG_COLOUR,
+ file_header->status_fg);
+ ro_gui_set_icon_selected_state(dialog_main, ICON_MAIN_THROBBER_LEFT,
+ file_header->throbber_left);
+
+ /* Remember the filename for saving later on
+ */
+ if (theme_filename) free(theme_filename);
+ theme_filename = malloc(strlen(path) + 1);
+ sprintf(theme_filename, "%s", path);
+ if (sprite_filename) free(sprite_filename);
+ sprite_filename = NULL;
+
+ /* Exit cleanly
+ */
+ free(file_header);
+ return true;
+}
+
+bool ro_gui_save_theme(char *path) {
+ os_error *error;
+ struct theme_file_header *file_header;
+ char *workspace;
+ int workspace_size, output_size, input_left, output_left;
+ squash_output_status status;
+
+ /* Get some memory
+ */
+ unsigned int file_size = sizeof (struct theme_file_header);
+ file_header = (struct theme_file_header *)calloc(file_size, 1);
+ if (!file_header) {
+ LOG(("No memory for calloc()"));
+ warn_user("NoMemory", 0);
+ return false;
+ }
+
+ /* Complete the header
+ */
+ file_header->magic_value = 0x4d54534e;
+ file_header->parser_version = 1;
+ strcpy(file_header->name, ro_gui_get_icon_string(dialog_main, ICON_MAIN_NAME));
+ strcpy(file_header->author, ro_gui_get_icon_string(dialog_main, ICON_MAIN_AUTHOR));
+ file_header->browser_bg = ro_gui_get_icon_background_colour(dialog_main,
+ ICON_MAIN_BROWSER_COLOUR);
+ file_header->hotlist_bg = ro_gui_get_icon_background_colour(dialog_main,
+ ICON_MAIN_HOTLIST_COLOUR);
+ file_header->status_bg = ro_gui_get_icon_background_colour(dialog_main,
+ ICON_MAIN_STATUSBG_COLOUR);
+ file_header->status_fg = ro_gui_get_icon_background_colour(dialog_main,
+ ICON_MAIN_STATUSFG_COLOUR);
+ if (ro_gui_get_icon_selected_state(dialog_main, ICON_MAIN_THROBBER_LEFT)) {
+ file_header->throbber_left = 0xff;
+ }
+
+ /* Append the compressed sprites
+ */
+ if (theme_sprites) {
+ file_header->decompressed_sprite_size = theme_sprites->size;
+ error = xsquash_compress_return_sizes(theme_sprites->size,
+ &workspace_size, &output_size);
+ if (error) {
+ free(file_header);
+ LOG(("xsquash_compress_return_sizes: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MiscError", error->errmess);
+ return false;
+ }
+ workspace = realloc(file_header,
+ file_size + output_size);
+ if (!workspace) {
+ free(file_header);
+ LOG(("No memory for realloc()"));
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ file_header = (struct theme_file_header *)workspace;
+ workspace = malloc(workspace_size);
+ if (!workspace) {
+ free(file_header);
+ LOG(("No memory for malloc()"));
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ error = xsquash_compress((squash_input_status)0,
+ workspace, (byte *)theme_sprites,
+ theme_sprites->size,
+ (byte *)(file_header + 1), output_size,
+ &status,
+ 0, &input_left, 0, &output_left);
+ free(workspace);
+ if (error) {
+ free(file_header);
+ LOG(("xsquash_compress: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MiscError", error->errmess);
+ return false;
+ }
+ if ((input_left > 0) || (status != 0)) {
+ free(file_header);
+ LOG(("Failed to complete compression with %i bytes left and status %i",
+ input_left, (int)status));
+ warn_user("FileError", 0);
+ return false;
+ }
+ file_header->compressed_sprite_size = output_size - output_left;
+ file_size += output_size - output_left;
+ }
+
+ /* Save the file
+ */
+ error = xosfile_save_stamped(path, (bits)0xffd,
+ (char *)file_header, ((char *)file_header) + file_size);
+ free(file_header);
+ if (error) {
+ LOG(("xosfile_save_stamped: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+ return false;
+}
+
+bool ro_gui_save_sprites(char *path) {
+ os_error *error;
+ error = xosspriteop_save_sprite_file(osspriteop_USER_AREA,
+ theme_sprites, path);
+ if (error) {
+ LOG(("xosfile_save_sprite_file: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("FileError", error->errmess);
+ return false;
+ }
+ if (sprite_filename) {
+ free(sprite_filename);
+ sprite_filename = NULL;
+ }
+ sprite_filename = malloc(strlen(path) + 1);
+ if (!sprite_filename) return true;
+ sprintf(sprite_filename, "%s", path);
+ return true;
+}
diff --git a/riscos/wimp.c b/riscos/wimp.c
new file mode 100644
index 0000000..bb97ec6
--- /dev/null
+++ b/riscos/wimp.c
@@ -0,0 +1,556 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * General RISC OS WIMP/OS library functions (implementation).
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "oslib/os.h"
+#include "oslib/osfile.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpextend.h"
+#include "oslib/wimpreadsysinfo.h"
+#include "oslib/wimpspriteop.h"
+#include "nstheme/desktop/gui.h"
+#include "nstheme/riscos/gui.h"
+#include "nstheme/riscos/wimp.h"
+#include "nstheme/utils/log.h"
+#include "nstheme/utils/utils.h"
+
+/* Wimp_Extend,11 block
+*/
+static wimpextend_furniture_sizes furniture_sizes;
+
+
+/**
+ * Gets the horzontal scrollbar height
+ */
+int ro_get_hscroll_height(wimp_w w) {
+ wimp_version_no version;
+
+ /* Read the hscroll height
+ */
+ if (!w) return 38;
+ furniture_sizes.w = w;
+ furniture_sizes.border_widths.y0 = 38;
+ xwimpextend_get_furniture_sizes(&furniture_sizes);
+
+ /* There is a quirk with the returned size as it differs between versions of the
+ WindowManager module. The incorrect height is returned by the version distributed
+ with the universal boot sequence (3.98) and presumably any previous version.
+ */
+ if (!xwimpreadsysinfo_version(&version)) {
+ if ((int)version <= 398) {
+ return furniture_sizes.border_widths.y0 + 2;
+ }
+ }
+
+ /* Return the standard (unhacked) size
+ */
+ return furniture_sizes.border_widths.y0;
+}
+
+
+/**
+ * Gets the vertical scrollbar width
+ */
+int ro_get_vscroll_width(wimp_w w) {
+
+ /* Read the hscroll height
+ */
+ if (!w) return 38;
+ furniture_sizes.w = w;
+ furniture_sizes.border_widths.x1 = 38;
+ xwimpextend_get_furniture_sizes(&furniture_sizes);
+
+ /* Return the standard (unhacked) size
+ */
+ return furniture_sizes.border_widths.x1;
+}
+
+
+/**
+ * Reads a modes EIG factors.
+ *
+ * \param mode mode to read EIG factors for, or -1 for current
+ */
+struct eig_factors ro_read_eig_factors(os_mode mode) {
+ bits psr;
+ struct eig_factors factors;
+ xos_read_mode_variable(mode, os_MODEVAR_XEIG_FACTOR, &factors.xeig, &psr);
+ xos_read_mode_variable(mode, os_MODEVAR_YEIG_FACTOR, &factors.yeig, &psr);
+ return factors;
+}
+
+
+/**
+ * Converts the supplied os_coord from OS units to pixels.
+ *
+ * \param os_units values to convert
+ * \param mode mode to use EIG factors for, or -1 for current
+ */
+void ro_convert_os_units_to_pixels(os_coord *os_units, os_mode mode) {
+ struct eig_factors factors = ro_read_eig_factors(mode);
+ os_units->x = (os_units->x >> factors.xeig);
+ os_units->y = (os_units->y >> factors.yeig);
+}
+
+
+/**
+ * Converts the supplied os_coord from pixels to OS units.
+ *
+ * \param pixels values to convert
+ * \param mode mode to use EIG factors for, or -1 for current
+ */
+void ro_convert_pixels_to_os_units(os_coord *pixels, os_mode mode) {
+ struct eig_factors factors = ro_read_eig_factors(mode);
+ pixels->x = (pixels->x << factors.xeig);
+ pixels->y = (pixels->y << factors.yeig);
+}
+
+
+/**
+ * Redraws an icon
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+#define ro_gui_redraw_icon(w, i) xwimp_set_icon_state(w, i, 0, 0)
+
+
+/**
+ * Read the contents of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \return string in icon
+ */
+char *ro_gui_get_icon_string(wimp_w w, wimp_i i) {
+ char *text;
+ int size;
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ if (xwimp_get_icon_state(&ic)) return NULL;
+ text = ic.icon.data.indirected_text.text;
+ size = ic.icon.data.indirected_text.size;
+ text[size - 1] = '\0';
+ while (text[0]) {
+ if (text[0] < 32) {
+ text[0] = '\0';
+ } else {
+ text++;
+ }
+ }
+ return ic.icon.data.indirected_text.text;
+}
+
+
+/**
+ * Set the contents of an icon to a string.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param text string (copied)
+ */
+void ro_gui_set_icon_string(wimp_w w, wimp_i i, const char *text) {
+ wimp_caret caret;
+ wimp_icon_state ic;
+ int old_len, len;
+
+ /* Get the icon data
+ */
+ ic.w = w;
+ ic.i = i;
+ if (xwimp_get_icon_state(&ic))
+ return;
+
+ /* Check that the existing text is not the same as the updated text
+ to stop flicker
+ */
+ if (ic.icon.data.indirected_text.size
+ && !strncmp(ic.icon.data.indirected_text.text, text,
+ (unsigned int)ic.icon.data.indirected_text.size - 1))
+ return;
+
+ /* Copy the text across
+ */
+ old_len = strlen(ic.icon.data.indirected_text.text);
+ if (ic.icon.data.indirected_text.size) {
+ strncpy(ic.icon.data.indirected_text.text, text,
+ (unsigned int)ic.icon.data.indirected_text.size - 1);
+ ic.icon.data.indirected_text.text[ic.icon.data.indirected_text.size - 1] = '\0';
+ }
+
+ /* Handle the caret being in the icon
+ */
+ if (!xwimp_get_caret_position(&caret)) {
+ if ((caret.w == w) && (caret.i == i)) {
+ len = strlen(text);
+ if ((caret.index > len) || (caret.index == old_len)) caret.index = len;
+ xwimp_set_caret_position(w, i, caret.pos.x, caret.pos.y, -1, caret.index);
+ }
+ }
+
+ /* Redraw the icon
+ */
+ ro_gui_redraw_icon(w, i);
+}
+
+
+/**
+ * Set the contents of an icon to a number.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param value value
+ */
+void ro_gui_set_icon_integer(wimp_w w, wimp_i i, int value) {
+ char buffer[20]; // Big enough for 64-bit int
+ sprintf(buffer, "%d", value);
+ ro_gui_set_icon_string(w, i, buffer);
+}
+
+
+/**
+ * Set the selected state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state selected state
+ */
+void ro_gui_set_icon_selected_state(wimp_w w, wimp_i i, bool state) {
+ os_error *error;
+ if (ro_gui_get_icon_selected_state(w, i) == state) return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_SELECTED : 0), wimp_ICON_SELECTED);
+ if (error) {
+ LOG(("xwimp_get_icon_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ }
+}
+
+/**
+ * Gets the selected state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_selected_state(wimp_w w, wimp_i i) {
+ os_error *error;
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG(("xwimp_get_icon_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return false;
+ }
+ return ((ic.icon.flags & wimp_ICON_SELECTED) != 0);
+}
+
+
+/**
+ * Set the shaded state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state selected state
+ */
+void ro_gui_set_icon_shaded_state(wimp_w w, wimp_i i, bool state) {
+ os_error *error;
+ if (ro_gui_get_icon_shaded_state(w, i) == state) return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_SHADED : 0), wimp_ICON_SHADED);
+ if (error) {
+ LOG(("xwimp_get_icon_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Gets the shaded state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_shaded_state(wimp_w w, wimp_i i) {
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ xwimp_get_icon_state(&ic);
+ return (ic.icon.flags & wimp_ICON_SHADED) != 0;
+}
+
+
+/**
+ * Set the background colour of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state selected state
+ */
+void ro_gui_set_icon_background_colour(wimp_w w, wimp_i i, int colour) {
+ os_error *error;
+ if (ro_gui_get_icon_background_colour(w, i) == colour) return;
+ error = xwimp_set_icon_state(w, i,
+ (colour <<wimp_ICON_BG_COLOUR_SHIFT), (15 << wimp_ICON_BG_COLOUR_SHIFT));
+ if (error) {
+ LOG(("xwimp_get_icon_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Gets the background colour of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+int ro_gui_get_icon_background_colour(wimp_w w, wimp_i i) {
+ os_error *error;
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG(("xwimp_get_icon_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return -1;
+ }
+ return ((ic.icon.flags >> wimp_ICON_BG_COLOUR_SHIFT) & 15);
+}
+
+
+/**
+ * Set a window title (does *not* redraw the title)
+ *
+ * \param w window handle
+ * \param text new title (copied)
+ */
+void ro_gui_set_window_title(wimp_w w, const char *text) {
+ wimp_window_info_base window;
+ os_error *error;
+
+ /* Get the window details
+ */
+ window.w = w;
+ error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
+ if (error) {
+ LOG(("xwimp_get_window_info: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Set the title string
+ */
+ strncpy(window.title_data.indirected_text.text, text,
+ (unsigned int)window.title_data.indirected_text.size - 1);
+ window.title_data.indirected_text.text[window.title_data.indirected_text.size - 1] = '\0';
+}
+
+
+/**
+ * Places the caret in the first available icon
+ */
+void ro_gui_set_caret_first(wimp_w w) {
+ int icon, button;
+ wimp_window_state win_state;
+ wimp_window_info_base window;
+ wimp_icon_state state;
+ wimp_caret caret;
+ os_error *error;
+
+ /* Check the window is open
+ */
+ win_state.w = w;
+ error = xwimp_get_window_state(&win_state);
+ if (error) {
+ LOG(("xwimp_get_window_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+ if (!(win_state.flags & wimp_WINDOW_OPEN)) return;
+
+ /* Check if the window already has the caret
+ */
+ if (!xwimp_get_caret_position(&caret)) {
+ if (caret.w == w) return;
+ }
+
+ /* Get the window details
+ */
+ window.w = w;
+ error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
+ if (error) {
+ LOG(("xwimp_get_window_info: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Work through our icons
+ */
+ state.w = w;
+ for (icon = 0; icon < window.icon_count; icon++) {
+ /* Get the icon state
+ */
+ state.i = icon;
+ error = xwimp_get_icon_state(&state);
+ if (error) {
+ LOG(("xwimp_get_icon_state: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* Ignore if it's shaded
+ */
+ if (state.icon.flags & wimp_ICON_SHADED)
+ continue;
+
+ /* Check if it's writable
+ */
+ button = (state.icon.flags >> wimp_ICON_BUTTON_TYPE_SHIFT) & 0xf;
+ if ((button == wimp_BUTTON_WRITE_CLICK_DRAG) ||
+ (button == wimp_BUTTON_WRITABLE)) {
+ error = xwimp_set_caret_position(w, icon, 0, 0, -1,
+ strlen(state.icon.data.indirected_text.text));
+ if (error) {
+ LOG(("xwimp_set_caret_position: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ }
+ return;
+ }
+ }
+}
+
+
+/**
+ * Opens a window at the centre of either another window or the screen
+ *
+ * /param parent the parent window (NULL for centre of screen)
+ * /param child the child window
+ */
+void ro_gui_open_window_centre(wimp_w parent, wimp_w child) {
+ os_error *error;
+ wimp_window_state state;
+ int mid_x, mid_y;
+ int dimension, scroll_width;
+
+ /* Get the parent window state
+ */
+ if (parent) {
+ state.w = parent;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+ scroll_width = ro_get_vscroll_width(parent);
+
+ /* Get the centre of the parent
+ */
+ mid_x = (state.visible.x0 + state.visible.x1 + scroll_width) / 2;
+ mid_y = (state.visible.y0 + state.visible.y1) / 2;
+ } else {
+ ro_gui_screen_size(&mid_x, &mid_y);
+ mid_x /= 2;
+ mid_y /= 2;
+ }
+
+ /* Get the child window state
+ */
+ state.w = child;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+ if (!(state.flags & wimp_WINDOW_OPEN)) {
+ /* Move to the centre of the parent at the top of the stack
+ */
+ dimension = state.visible.x1 - state.visible.x0;
+ scroll_width = ro_get_vscroll_width(child);
+ state.visible.x0 = mid_x - (dimension + scroll_width) / 2;
+ state.visible.x1 = state.visible.x0 + dimension;
+ dimension = state.visible.y1 - state.visible.y0;
+ state.visible.y0 = mid_y - dimension / 2;
+ state.visible.y1 = state.visible.y0 + dimension;
+ }
+ state.next = wimp_TOP;
+ wimp_open_window((wimp_open *) &state);
+}
+
+
+
+/**
+ * Load a sprite file into memory.
+ *
+ * \param pathname file to load
+ * \return sprite area, or 0 on memory exhaustion or error and error reported
+ */
+
+osspriteop_area *ro_gui_load_sprite_file(const char *pathname)
+{
+ int len;
+ fileswitch_object_type obj_type;
+ osspriteop_area *area;
+ os_error *error;
+
+ error = xosfile_read_stamped_no_path(pathname,
+ &obj_type, 0, 0, &len, 0, 0);
+ if (error) {
+ LOG(("xosfile_read_stamped_no_path: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MiscError", error->errmess);
+ return 0;
+ }
+ if (obj_type != fileswitch_IS_FILE) {
+ warn_user("FileError", pathname);
+ return 0;
+ }
+
+ area = malloc(len + 4);
+ if (!area) {
+ warn_user("NoMemory", 0);
+ return 0;
+ }
+
+ area->size = len + 4;
+ area->sprite_count = 0;
+ area->first = 16;
+ area->used = 16;
+
+ error = xosspriteop_load_sprite_file(osspriteop_USER_AREA,
+ area, pathname);
+ if (error) {
+ LOG(("xosspriteop_load_sprite_file: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("MiscError", error->errmess);
+ free(area);
+ return 0;
+ }
+
+ return area;
+}
diff --git a/riscos/wimp.h b/riscos/wimp.h
new file mode 100644
index 0000000..33c3ae7
--- /dev/null
+++ b/riscos/wimp.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ */
+
+/** \file
+ * General RISC OS WIMP/OS library functions (interface).
+ */
+
+
+#ifndef _NETSURF_RISCOS_WIMP_H_
+#define _NETSURF_RISCOS_WIMP_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/wimp.h"
+
+struct eig_factors {
+ int xeig;
+ int yeig;
+};
+
+
+int ro_get_hscroll_height(wimp_w w);
+int ro_get_vscroll_width(wimp_w w);
+struct eig_factors ro_read_eig_factors(os_mode mode);
+void ro_convert_os_units_to_pixels(os_coord *os_units, os_mode mode);
+void ro_convert_pixels_to_os_units(os_coord *pixels, os_mode mode);
+
+#define ro_gui_redraw_icon(w, i) xwimp_set_icon_state(w, i, 0, 0)
+char *ro_gui_get_icon_string(wimp_w w, wimp_i i);
+void ro_gui_set_icon_string(wimp_w w, wimp_i i, const char *text);
+void ro_gui_set_icon_integer(wimp_w w, wimp_i i, int value);
+void ro_gui_set_icon_selected_state(wimp_w w, wimp_i i, bool state);
+bool ro_gui_get_icon_selected_state(wimp_w w, wimp_i i);
+void ro_gui_set_icon_shaded_state(wimp_w w, wimp_i i, bool state);
+bool ro_gui_get_icon_shaded_state(wimp_w w, wimp_i i);
+int ro_gui_get_icon_background_colour(wimp_w w, wimp_i i);
+void ro_gui_set_icon_background_colour(wimp_w w, wimp_i i, int colour);
+void ro_gui_set_window_title(wimp_w w, const char *title);
+void ro_gui_set_caret_first(wimp_w w);
+void ro_gui_open_window_centre(wimp_w parent, wimp_w child);
+
+osspriteop_area *ro_gui_load_sprite_file(const char *pathname);
+#endif
diff --git a/utils/log.h b/utils/log.h
new file mode 100644
index 0000000..65417b9
--- /dev/null
+++ b/utils/log.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2003 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
+ */
+
+#include <stdio.h>
+
+#ifndef _NETSURF_LOG_H_
+#define _NETSURF_LOG_H_
+
+#ifdef NDEBUG
+# define LOG(x) ((void) 0)
+#else
+# ifdef __GNUC__
+# define LOG(x) (printf(__FILE__ " %s %i: ", __PRETTY_FUNCTION__, __LINE__), printf x, fputc('\n', stdout))
+# elif defined(__CC_NORCROFT)
+# define LOG(x) (printf(__FILE__ " %s %i: ", __func__, __LINE__), printf x, fputc('\n', stdout))
+# else
+# define LOG(x) (printf(__FILE__ " %i: ", __LINE__), printf x, fputc('\n', stdout))
+# endif
+#endif
+
+#endif
diff --git a/utils/messages.c b/utils/messages.c
new file mode 100644
index 0000000..7cc10bb
--- /dev/null
+++ b/utils/messages.c
@@ -0,0 +1,169 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ */
+
+/** \file
+ * Localised message support (implementation).
+ *
+ * Native language messages are loaded from a file and stored hashed by key for
+ * fast access.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "nstheme/utils/log.h"
+#include "nstheme/utils/messages.h"
+#include "nstheme/utils/utils.h"
+
+/** We store the messages in a fixed-size hash table. */
+#define HASH_SIZE 77
+
+/** Maximum length of a key. */
+#define MAX_KEY_LENGTH 24
+
+/** Entry in the messages hash table. */
+struct messages_entry {
+ struct messages_entry *next; /**< Next entry in this hash chain. */
+ char key[MAX_KEY_LENGTH];
+ char value[1];
+};
+
+/** Localised messages hash table. */
+static struct messages_entry *messages_table[HASH_SIZE];
+
+
+static unsigned int messages_hash(const char *s);
+
+
+/**
+ * Read keys and values from messages file.
+ *
+ * \param path pathname of messages file
+ *
+ * The messages are merged with any previously loaded messages. Any keys which
+ * are present already are replaced with the new value.
+ *
+ * Exits through die() in case of error.
+ */
+
+void messages_load(const char *path)
+{
+ char s[300];
+ FILE *fp;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ snprintf(s, sizeof s, "Unable to open messages file "
+ "\"%.100s\": %s", path, strerror(errno));
+ s[sizeof s - 1] = 0;
+ LOG(("%s", s));
+ die(s);
+ }
+
+ while (fgets(s, sizeof s, fp)) {
+ char *colon, *value;
+ unsigned int slot;
+ struct messages_entry *entry;
+ size_t length;
+
+ if (s[0] == 0 || s[0] == '#')
+ continue;
+
+ s[strlen(s) - 1] = 0; /* remove \n at end */
+ colon = strchr(s, ':');
+ if (!colon)
+ continue;
+ *colon = 0; /* terminate key */
+ value = colon + 1;
+ length = strlen(value);
+
+ entry = malloc(sizeof *entry + length + 1);
+ if (!entry) {
+ snprintf(s, sizeof s, "Not enough memory to load "
+ "messages file \"%.100s\".", path);
+ s[sizeof s - 1] = 0;
+ LOG(("%s", s));
+ die(s);
+ }
+ strncpy(entry->key, s, MAX_KEY_LENGTH);
+ strcpy(entry->value, value);
+ slot = messages_hash(entry->key);
+ entry->next = messages_table[slot];
+ messages_table[slot] = entry;
+ }
+
+ fclose(fp);
+}
+
+
+/**
+ * Fast lookup of a message by key.
+ *
+ * \param key key of message
+ * \return value of message, or key if not found
+ */
+
+const char *messages_get(const char *key)
+{
+ struct messages_entry *entry;
+
+ for (entry = messages_table[messages_hash(key)];
+ entry && strcasecmp(entry->key, key) != 0;
+ entry = entry->next)
+ ;
+ if (!entry)
+ return key;
+ return entry->value;
+}
+
+/**
+ * Retrieve the key associated with a value
+ *
+ * \param value The value as returned by messages_get
+ * \return The key associated with the value or NULL if not found
+ */
+const char *messages_get_key(const char *value)
+{
+ const char *key = value - MAX_KEY_LENGTH;
+ const char *temp_value = messages_get(key);
+
+ if (strcmp(value, temp_value) == 0)
+ return key;
+
+ return NULL;
+}
+
+
+/**
+ * Hash function for keys.
+ */
+
+unsigned int messages_hash(const char *s)
+{
+ unsigned int i, z = 0;
+ if (!s)
+ return 0;
+ for (i = 0; i != MAX_KEY_LENGTH && s[i]; i++)
+ z += s[i] & 0x1f; /* lower 5 bits, case insensitive */
+ return z % HASH_SIZE;
+}
+
+
+/**
+ * Dump set of loaded messages.
+ */
+
+void messages_dump(void)
+{
+ unsigned int i;
+ for (i = 0; i != HASH_SIZE; i++) {
+ struct messages_entry *entry;
+ for (entry = messages_table[i]; entry; entry = entry->next)
+ printf("%.20s:%s\n", entry->key, entry->value);
+ }
+}
diff --git a/utils/messages.h b/utils/messages.h
new file mode 100644
index 0000000..33b2fc3
--- /dev/null
+++ b/utils/messages.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ */
+
+/** \file
+ * Localised message support (interface).
+ *
+ * The messages module loads a file of keys and associated strings, and
+ * provides fast lookup by key. The messages file consists of key:value lines,
+ * comment lines starting with #, and other lines are ignored. Use
+ * messages_load() to read the file into memory. To lookup a key, use
+ * messages_get("key").
+ *
+ * Only the first MAX_KEY_LENGTH (currently 24) characters of the key are
+ * significant.
+ */
+
+#ifndef _NETSURF_UTILS_MESSAGES_H_
+#define _NETSURF_UTILS_MESSAGES_H_
+
+void messages_load(const char *path);
+const char *messages_get(const char *key);
+const char *messages_get_key(const char *value);
+void messages_dump(void);
+
+#endif
diff --git a/utils/utils.c b/utils/utils.c
new file mode 100644
index 0000000..4e7d80f
--- /dev/null
+++ b/utils/utils.c
@@ -0,0 +1,30 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "nstheme/utils/utils.h"
+
+/**
+ * Check if a directory exists.
+ */
+
+bool is_dir(const char *path)
+{
+ struct stat s;
+
+ if (stat(path, &s))
+ return false;
+
+ return S_ISDIR(s.st_mode) ? true : false;
+}
diff --git a/utils/utils.h b/utils/utils.h
new file mode 100644
index 0000000..7f52acf
--- /dev/null
+++ b/utils/utils.h
@@ -0,0 +1,23 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
+ */
+
+#ifndef _NETSURF_UTILS_UTILS_H_
+#define _NETSURF_UTILS_UTILS_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+#include "libxml/encoding.h"
+
+void die(const char * const error);
+bool is_dir(const char *path);
+void warn_user(const char *warning, const char *detail);
+
+#endif