> Index: gtk/gtk_theme.c > this file refers to the largest of the branches, customization/theming; currently a theme is a set of button images; though adaptations are possible; themes may be downloaded, a content-type served as x/application-netsurf-theme, containerized as described in utils/container.c, is automatically installed as a theme [as long as the images are properly named]; a directory full of images, added to the themes folder, may be added as a theme from the gtk preferences window too; theme_init loads a saved selected theme from its folder, implements it into every gtk window; theme_add adds the theme name to the list of selectable themes; theme_verify maintains the integrity of the list; theme_implement changes the images visible in the gtk window; theme_load loads a set of images into an array for adding to buttons; to save trouble involving gtk object counts [particularly deletion of a button during customization removes the image reference] a fresh array is loaded from the pixbuf cache every time a set of buttons is destined for theme_implement; theme_prepare caches pixbufs from the image files to avoid multiple filesystem calls; theme_image_default returns default images when no theme is selected / when a theme has no image for a button that has its default image set; the theme_install_* functions are gtk-specific functions to respond when the appropriate content-type is being fetched; > =================================================================== > --- /dev/null 2009-04-16 19:17:07.000000000 +0100 > +++ gtk/gtk_theme.c 2009-07-10 12:49:36.000000000 +0100 > @@ -0,0 +1,591 @@ > +/* > + * Copyright 2009 Mark Benjamin <[email protected]> > + * > + * This file is part of NetSurf, http://www.netsurf-browser.org/ > + * > + * NetSurf is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; version 2 of the License. > + * > + * NetSurf is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <gtk/gtk.h> > +#include <stdio.h> > +#include <sys/stat.h> > +#include "content/content.h" > +#include "content/content_type.h" > +#include "gtk/gtk_gui.h" > +#include "gtk/gtk_scaffolding.h" > +#include "gtk/gtk_menu.h" > +#include "gtk/gtk_theme.h" > +#include "gtk/gtk_window.h" > +#include "gtk/options.h" > +#include "gtk/dialogs/gtk_options.h" > +#include "utils/container.h" > +#include "utils/log.h" > +#include "utils/messages.h" > +#include "utils/utils.h" > + > +struct nsgtk_theme_cache { > + GdkPixbuf *image[PLACEHOLDER_BUTTON]; > + GdkPixbuf *searchimage[3]; /* back, forward, close */ > + /* apng throbber image */ > +}; > + > +char *current_theme_name = NULL; > +static struct nsgtk_theme_cache *theme_cache_menu = NULL; > +static struct nsgtk_theme_cache *theme_cache_toolbar = NULL; > + > +static struct nsgtk_theme *nsgtk_theme_default(GtkIconSize s); > +static GtkImage *nsgtk_theme_image_default(int i, GtkIconSize s); > +static bool nsgtk_theme_verify(const char *themename); > + > +#ifdef WITH_THEME_INSTALL > +static struct content *theme_install_content = NULL; > + > +static void theme_install_callback(content_msg msg, struct content *c, > + intptr_t p1, intptr_t p2, union content_msg_data data); > +static bool theme_install_read(char *data, unsigned long len); > +#endif > + > +void nsgtk_theme_init() > +{ > + if (option_current_theme == 0) > + return; > + char *themefile = g_strconcat(res_dir_location, "themelist", NULL); > + nsgtk_scaffolding *list = scaf_list; > + nsgtk_theme_verify(NULL); > + FILE *fp = fopen(themefile, "r"); > + char buf[50]; > + int row_count = 0; > + if (fp == NULL) > + return; > + while (fgets(buf, sizeof(buf), fp)) { > + if (buf[0] == '\0') > + continue; > + > + if (row_count++ == option_current_theme) { > + if (current_theme_name != NULL) > + free(current_theme_name); > + buf[strlen(buf) - 1] = '\0'; > + current_theme_name = strdup(buf); > + break; > + } > + } > + fclose(fp); > + > + while (list) { > + nsgtk_theme_implement(list); > + list = nsgtk_scaffolding_iterate(list); > + } > +} > + > +void nsgtk_theme_add(const char *themename) > +{ > + GtkWidget *notification, *label; > + char *labelcontent, *themefile = g_strconcat(res_dir_location, > + "themelist", NULL); > + /* conduct verification here; no adding duplicates to list */ > + if (nsgtk_theme_verify(themename) == false) { > + warn_user(messages_get("gtkThemeDup"), 0); > + g_free(themefile); > + return; > + } > + FILE *fp = fopen(themefile, "a"); > + if (fp == NULL) { > + warn_user(messages_get("gtkFileError"), themefile); > + g_free(themefile); > + return; > + } > + fprintf(fp, "%s\n", themename); > + fclose(fp); > + g_free(themefile); > + > + /* notification that theme was added successfully */ > + notification = gtk_dialog_new_with_buttons(messages_get("gtkThemeAdd"), > + NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, > + GTK_RESPONSE_NONE, NULL); > + labelcontent = g_strconcat("\t\t\t", messages_get("gtkThemeAdd"), > + "\t\t\t", NULL); > + label = gtk_label_new(labelcontent); > + g_signal_connect_swapped(notification, "response", > + G_CALLBACK(gtk_widget_destroy), notification); > + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(notification)->vbox), label); > + gtk_widget_show_all(notification); > + g_free(labelcontent); > + > + /* update combo */ > + if (wndPreferences == NULL) > + return; > + nsgtk_options_combo_theme_add(themename); > + > +} > + > +bool nsgtk_theme_verify(const char *themename) > +{ > + long filelength; > + FILE *fp; > + size_t val; > + char buf[50]; > + char *themefile = g_strconcat(res_dir_location, "themelist", NULL); > + if (themename == NULL) { > + char *filecontent, *testfile; > + struct stat sta; > + fp = fopen(themefile, "r+"); > + if (fp == NULL) { > + warn_user(messages_get("gtkFileError"), themefile); > + g_free(themefile); > + return true; > + } > + fseek(fp, 0L, SEEK_END); > + filelength = ftell(fp); > + filecontent = malloc(filelength + 2); > + strcpy(filecontent, "gtk default theme\n"); > + if (filecontent == NULL) { > + warn_user(messages_get("NoMemory"), 0); > + g_free(themefile); > + return true; > + } > + fseek(fp, 0L, SEEK_SET); > + while (fgets(buf, sizeof(buf), fp)) { > + /* iterate list */ > + buf[strlen(buf) - 1] = '\0'; > + testfile = g_strconcat(res_dir_location, "themes/", > + buf, NULL); > + /* check every directory */ > + if (access(testfile, R_OK) == 0) { > + stat(testfile, &sta); > + if (S_ISDIR(sta.st_mode)) { > + free(testfile); > + buf[strlen(buf)] = '\n'; > + strcat(filecontent, buf); > + } > + } > + } > + fclose(fp); > + fp = fopen(themefile, "w"); > + if (fp == NULL) { > + warn_user(messages_get("gtkFileError"), themefile); > + free(filecontent); > + g_free(themefile); > + return true; > + } > + val = fwrite(filecontent, strlen(filecontent), 1, fp); > + if (val == 0) > + LOG(("empty write themelist")); > + fclose(fp); > + free(filecontent); > + g_free(themefile); > + return true; > + } else { > + fp = fopen(themefile, "r"); > + if (fp == NULL) { > + warn_user(messages_get("gtkFileError"), themefile); > + g_free(themefile); > + return false; > + } > + while (fgets(buf, sizeof(buf), fp)) { > + buf[strlen(buf) - 1] = '\0'; > + if (strcmp(buf, themename) == 0) { > + g_free(themefile); > + return false; > + } > + } > + fclose(fp); > + g_free(themefile); > + return true; > + } > + > +} > + > +void nsgtk_theme_implement(struct gtk_scaffolding *g) > +{ > + struct nsgtk_theme *theme[4]; > + int i; > + for (i = 0; i < 3; i++) > + theme[i] = nsgtk_theme_load(GTK_ICON_SIZE_MENU); > + theme[3] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR); > + for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) { > + if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) || > + (i == WEBSEARCH_ITEM)) > + continue; > + if (nsgtk_scaffolding_button(g, i)->main != NULL) { > + gtk_image_menu_item_set_image(nsgtk_scaffolding_button( > + g, i)->main, > + GTK_WIDGET(theme[0]->image[i])); > + gtk_widget_show_all(GTK_WIDGET( > + nsgtk_scaffolding_button(g, i)->main)); > + } > + if (nsgtk_scaffolding_button(g, i)->rclick != NULL) { > + gtk_image_menu_item_set_image(nsgtk_scaffolding_button( > + g, i)->rclick, > + GTK_WIDGET(theme[1]->image[i])); > + gtk_widget_show_all(GTK_WIDGET( > + nsgtk_scaffolding_button( > + g, i)->rclick)); > + } > + if (nsgtk_scaffolding_button(g, i)->popup != NULL) { > + gtk_image_menu_item_set_image(nsgtk_scaffolding_button( > + g, i)->popup, > + GTK_WIDGET(theme[2]->image[i])); > + gtk_widget_show_all(GTK_WIDGET( > + nsgtk_scaffolding_button(g, i)-> > + popup)); > + } > + if ((nsgtk_scaffolding_button(g, i)->location != -1) && > + (nsgtk_scaffolding_button(g, i)->button > + != NULL)) { > + gtk_tool_button_set_icon_widget( > + GTK_TOOL_BUTTON( > + nsgtk_scaffolding_button( > + g, i)->button), > + GTK_WIDGET(theme[3]->image[i])); > + gtk_widget_show_all(GTK_WIDGET( > + nsgtk_scaffolding_button(g, i)-> > + button)); > + } > + } > + > + /* set search bar images */ > + gtk_tool_button_set_icon_widget( > + nsgtk_scaffolding_search(g)->buttons[0], > + GTK_WIDGET(theme[0]->searchimage[0])); > + gtk_widget_show_all(GTK_WIDGET( > + nsgtk_scaffolding_search(g)->buttons[0])); > + gtk_tool_button_set_icon_widget( > + nsgtk_scaffolding_search(g)->buttons[1], > + GTK_WIDGET(theme[0]->searchimage[1])); > + gtk_widget_show_all(GTK_WIDGET( > + nsgtk_scaffolding_search(g)->buttons[1])); > + gtk_tool_button_set_icon_widget( > + nsgtk_scaffolding_search(g)->buttons[2], > + GTK_WIDGET(theme[0]->searchimage[2])); > + gtk_widget_show_all(GTK_WIDGET( > + nsgtk_scaffolding_search(g)->buttons[2])); > + > + for (i = 0; i < 4; i++) > + free(theme[i]); > +} > + > +struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s) > +{ > + if (current_theme_name == NULL) > + return nsgtk_theme_default(s); > + struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme)); > + if ((theme_cache_menu == NULL) || (theme_cache_toolbar == NULL)) > + nsgtk_theme_prepare(); > + /* load theme from cache */ > + struct nsgtk_theme_cache *cachetheme = (s == GTK_ICON_SIZE_MENU) ? > + theme_cache_menu : theme_cache_toolbar; > +#define SET_BUTTON_IMAGE(q)\ > + if (cachetheme->image[q##_BUTTON] != NULL)\ > + theme->image[q##_BUTTON] = GTK_IMAGE(gtk_image_new_from_pixbuf(\ > + cachetheme->image[q##_BUTTON]));\ > + else\ > + theme->image[q##_BUTTON] = nsgtk_theme_image_default(\ > + q##_BUTTON, s); > + SET_BUTTON_IMAGE(BACK) > + SET_BUTTON_IMAGE(HISTORY) > + SET_BUTTON_IMAGE(FORWARD) > + SET_BUTTON_IMAGE(STOP) > + SET_BUTTON_IMAGE(RELOAD) > + SET_BUTTON_IMAGE(HOME) > + SET_BUTTON_IMAGE(NEWWINDOW) > + SET_BUTTON_IMAGE(NEWTAB) > + SET_BUTTON_IMAGE(OPENFILE) > + SET_BUTTON_IMAGE(CLOSETAB) > + SET_BUTTON_IMAGE(CLOSEWINDOW) > + SET_BUTTON_IMAGE(SAVEPAGE) > + SET_BUTTON_IMAGE(PRINTPREVIEW) > + SET_BUTTON_IMAGE(PRINT) > + SET_BUTTON_IMAGE(QUIT) > + SET_BUTTON_IMAGE(CUT) > + SET_BUTTON_IMAGE(COPY) > + SET_BUTTON_IMAGE(PASTE) > + SET_BUTTON_IMAGE(DELETE) > + SET_BUTTON_IMAGE(SELECTALL) > + SET_BUTTON_IMAGE(PREFERENCES) > + SET_BUTTON_IMAGE(ZOOMPLUS) > + SET_BUTTON_IMAGE(ZOOMMINUS) > + SET_BUTTON_IMAGE(ZOOMNORMAL) > + SET_BUTTON_IMAGE(FULLSCREEN) > + SET_BUTTON_IMAGE(VIEWSOURCE) > + SET_BUTTON_IMAGE(CONTENTS) > + SET_BUTTON_IMAGE(ABOUT) > + SET_BUTTON_IMAGE(PDF) > + SET_BUTTON_IMAGE(PLAINTEXT) > + SET_BUTTON_IMAGE(DRAWFILE) > + SET_BUTTON_IMAGE(POSTSCRIPT) > + SET_BUTTON_IMAGE(FIND) > + SET_BUTTON_IMAGE(DOWNLOADS) > + SET_BUTTON_IMAGE(SAVEWINDOWSIZE) > + SET_BUTTON_IMAGE(TOGGLEDEBUGGING) > + SET_BUTTON_IMAGE(SAVEBOXTREE) > + SET_BUTTON_IMAGE(SAVEDOMTREE) > + SET_BUTTON_IMAGE(LOCALHISTORY) > + SET_BUTTON_IMAGE(GLOBALHISTORY) > + SET_BUTTON_IMAGE(ADDBOOKMARKS) > + SET_BUTTON_IMAGE(SHOWBOOKMARKS) > + SET_BUTTON_IMAGE(OPENLOCATION) > + SET_BUTTON_IMAGE(NEXTTAB) > + SET_BUTTON_IMAGE(PREVTAB) > + SET_BUTTON_IMAGE(GUIDE) > + SET_BUTTON_IMAGE(INFO) > +#undef SET_BUTTON_IMAGE > +#define SET_BUTTON_IMAGE(p, q)\ > + if (cachetheme->searchimage[p] != NULL)\ > + theme->searchimage[p] = GTK_IMAGE(gtk_image_new_from_pixbuf(\ > + cachetheme->searchimage[p]));\ > + else if (cachetheme->image[q##_BUTTON] != NULL)\ > + theme->searchimage[p] = GTK_IMAGE(gtk_image_new_from_pixbuf(\ > + cachetheme->image[q##_BUTTON]));\ > + else\ > + theme->searchimage[p] = nsgtk_theme_image_default(\ > + PLACEHOLDER_BUTTON + p, s); > + SET_BUTTON_IMAGE(0, BACK) > + SET_BUTTON_IMAGE(1, FORWARD) > + SET_BUTTON_IMAGE(2, CLOSEWINDOW) > +#undef SET_BUTTON_IMAGE > + return theme; > +} > + > +void nsgtk_theme_prepare(void) > +{ > + if (current_theme_name == NULL) > + return; > + if (theme_cache_menu == NULL) > + theme_cache_menu = malloc(sizeof(struct nsgtk_theme_cache)); > + if (theme_cache_toolbar == NULL) > + theme_cache_toolbar = malloc(sizeof(struct nsgtk_theme_cache)); > + char *path = g_strconcat(res_dir_location, "themes/", > + current_theme_name, "/", NULL); > + char *filename; > +#define CACHE_IMAGE(p, q)\ > + filename = g_strconcat(path, #q, ".png", NULL);\ > + theme_cache_toolbar->image[p##_BUTTON] =\ > + gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\ > + NULL);\ > + theme_cache_menu->image[p##_BUTTON] =\ > + gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\ > + NULL);\ > + g_free(filename) > + CACHE_IMAGE(BACK, back); > + CACHE_IMAGE(HISTORY, history); > + CACHE_IMAGE(FORWARD, forward); > + CACHE_IMAGE(STOP, stop); > + CACHE_IMAGE(RELOAD, reload); > + CACHE_IMAGE(HOME, home); > + CACHE_IMAGE(NEWWINDOW, newwindow); > + CACHE_IMAGE(NEWTAB, newtab); > + CACHE_IMAGE(OPENFILE, openfile); > + CACHE_IMAGE(CLOSETAB, closetab); > + CACHE_IMAGE(CLOSEWINDOW, closewindow); > + CACHE_IMAGE(SAVEPAGE, savepage); > + CACHE_IMAGE(PRINTPREVIEW, printpreview); > + CACHE_IMAGE(PRINT, print); > + CACHE_IMAGE(QUIT, quit); > + CACHE_IMAGE(CUT, cut); > + CACHE_IMAGE(COPY, copy); > + CACHE_IMAGE(PASTE, paste); > + CACHE_IMAGE(DELETE, delete); > + CACHE_IMAGE(SELECTALL, selectall); > + CACHE_IMAGE(PREFERENCES, preferences); > + CACHE_IMAGE(ZOOMPLUS, zoomplus); > + CACHE_IMAGE(ZOOMMINUS, zoomminus); > + CACHE_IMAGE(ZOOMNORMAL, zoomnormal); > + CACHE_IMAGE(FULLSCREEN, fullscreen); > + CACHE_IMAGE(VIEWSOURCE, viewsource); > + CACHE_IMAGE(CONTENTS, helpcontents); > + CACHE_IMAGE(ABOUT, helpabout); > + CACHE_IMAGE(PDF, pdf); > + CACHE_IMAGE(PLAINTEXT, plaintext); > + CACHE_IMAGE(DRAWFILE, drawfile); > + CACHE_IMAGE(POSTSCRIPT, postscript); > + CACHE_IMAGE(FIND, find); > + CACHE_IMAGE(DOWNLOADS, downloads); > + CACHE_IMAGE(SAVEWINDOWSIZE, savewindowsize); > + CACHE_IMAGE(TOGGLEDEBUGGING, toggledebugging); > + CACHE_IMAGE(SAVEBOXTREE, boxtree); > + CACHE_IMAGE(SAVEDOMTREE, domtree); > + CACHE_IMAGE(LOCALHISTORY, localhistory); > + CACHE_IMAGE(GLOBALHISTORY, globalhistory); > + CACHE_IMAGE(ADDBOOKMARKS, addbookmarks); > + CACHE_IMAGE(SHOWBOOKMARKS, showbookmarks); > + CACHE_IMAGE(OPENLOCATION, openlocation); > + CACHE_IMAGE(NEXTTAB, nexttab); > + CACHE_IMAGE(PREVTAB, prevtab); > + CACHE_IMAGE(GUIDE, helpguide); > + CACHE_IMAGE(INFO, helpinfo); > +#undef CACHE_IMAGE > +#define CACHE_IMAGE(p, q)\ > + filename = g_strconcat(path, #q, ".png", NULL);\ > + theme_cache_toolbar->searchimage[p] =\ > + gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\ > + NULL);\ > + theme_cache_menu->searchimage[p] =\ > + gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\ > + NULL);\ > + g_free(filename) > + CACHE_IMAGE(0, searchback); > + CACHE_IMAGE(1, searchforward); > + CACHE_IMAGE(2, searchclose); > +#undef CACHE_IMAGE > + g_free(path); > +} > + > +GtkImage *nsgtk_theme_image_default(int i, GtkIconSize s) > +{ > + char *imagefile; > + GtkImage *image; > + switch(i) { > +#define BUTTON_IMAGE(p, q)\ > + case p##_BUTTON:\ > + return GTK_IMAGE(gtk_image_new_from_stock(#q, s)) > + BUTTON_IMAGE(BACK, gtk-go-back); > + case HISTORY_BUTTON: > + imagefile = g_strconcat(res_dir_location, > + "arrow_down_8x32.png", NULL); > + image = GTK_IMAGE(gtk_image_new_from_file(imagefile)); > + g_free(imagefile); > + return image; > + BUTTON_IMAGE(FORWARD, gtk-go-forward); > + BUTTON_IMAGE(STOP, gtk-stop); > + BUTTON_IMAGE(RELOAD, gtk-refresh); > + BUTTON_IMAGE(HOME, gtk-home); > + BUTTON_IMAGE(NEWWINDOW, gtk-new); > + BUTTON_IMAGE(NEWTAB, gtk-new); > + BUTTON_IMAGE(OPENFILE, gtk-open); > + BUTTON_IMAGE(CLOSETAB, gtk-close); > + BUTTON_IMAGE(CLOSEWINDOW, gtk-close); > + BUTTON_IMAGE(SAVEPAGE, gtk-save-as); > + BUTTON_IMAGE(PRINTPREVIEW, gtk-print-preview); > + BUTTON_IMAGE(PRINT, gtk-print); > + BUTTON_IMAGE(QUIT, gtk-quit); > + BUTTON_IMAGE(CUT, gtk-cut); > + BUTTON_IMAGE(COPY, gtk-copy); > + BUTTON_IMAGE(PASTE, gtk-paste); > + BUTTON_IMAGE(DELETE, gtk-delete); > + BUTTON_IMAGE(SELECTALL, gtk-select-all); > + BUTTON_IMAGE(FIND, gtk-find); > + BUTTON_IMAGE(PREFERENCES, gtk-preferences); > + BUTTON_IMAGE(ZOOMPLUS, gtk-zoom-in); > + BUTTON_IMAGE(ZOOMMINUS, gtk-zoom-out); > + BUTTON_IMAGE(ZOOMNORMAL, gtk-zoom-100); > + BUTTON_IMAGE(FULLSCREEN, gtk-fullscreen); > + BUTTON_IMAGE(VIEWSOURCE, gtk-index); > + BUTTON_IMAGE(CONTENTS, gtk-help); > + BUTTON_IMAGE(ABOUT, gtk-about); > +#undef BUTTON_IMAGE > + case (PLACEHOLDER_BUTTON): > + return GTK_IMAGE(gtk_image_new_from_stock("gtk-go-back", s)); > + case (PLACEHOLDER_BUTTON + 1): > + return GTK_IMAGE(gtk_image_new_from_stock("gtk-go-forward", > + s)); > + case (PLACEHOLDER_BUTTON + 2): > + return GTK_IMAGE(gtk_image_new_from_stock("gtk-close", s)); > + default: > + imagefile = g_strconcat(res_dir_location, "themes/Alpha.png", > + NULL); > + image = GTK_IMAGE(gtk_image_new_from_file(imagefile)); > + g_free(imagefile); > + return image; > + } > +} > + > + > +#ifdef WITH_THEME_INSTALL > +/** > + * Handle CONTENT_THEME > + */ > +void theme_install_start(struct content *c) > +{ > + assert(c); > + assert(c->type == CONTENT_THEME); > + > + /* stop theme sitting in memory cache */ > + c->fresh = false; > + if (!content_add_user(c, theme_install_callback, 0, 0)) { > + warn_user("NoMemory", 0); > + return; > + } > +} > + > + > +/** > + * Callback for fetchcache() for theme install fetches. > + */ > +void theme_install_callback(content_msg msg, struct content *c, > + intptr_t p1, intptr_t p2, union content_msg_data data) > +{ > + switch (msg) { > + case CONTENT_MSG_READY: > + break; > + > + case CONTENT_MSG_DONE: > + theme_install_content = c; > + if (!theme_install_read(c->source_data, c->source_size)) > + warn_user("ThemeInvalid", 0); > + break; > + > + case CONTENT_MSG_ERROR: > + warn_user(data.error, 0); > + break; > + > + case CONTENT_MSG_STATUS: > + break; > + > + case CONTENT_MSG_LOADING: > + case CONTENT_MSG_REFORMAT: > + case CONTENT_MSG_REDRAW: > + case CONTENT_MSG_NEWPTR: > + case CONTENT_MSG_LAUNCH: > + case CONTENT_MSG_AUTH: > + default: > + assert(0); > + break; > + } > +} > + > +/** > + * handler saves theme data as a local theme > + */ > +bool theme_install_read(char *data, unsigned long len) > +{ > + char *filename; > + int handle = g_file_open_tmp("nsgtkthemeXXXXXX", &filename, NULL); > + ssize_t written = write(handle, data, len); > + close(handle); > + if ((unsigned)written != len) > + return false; > + > + /* get name of theme; set as dirname */ > + char *dirname = g_strconcat(res_dir_location, "themes/", NULL); > + if (dirname == NULL) > + return false; > + > + /* save individual files in theme */ > + filename = container_extract_theme(filename, dirname); > + g_free(dirname); > + if (filename == NULL) > + return false; > + nsgtk_theme_add(filename); > + > + return true; > +} > +#endif > + > +struct nsgtk_theme *nsgtk_theme_default(GtkIconSize s) > +{ > + struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme)); > + for (int i = BACK_BUTTON; i <= PLACEHOLDER_BUTTON + 2; i++) > + theme->image[i] = nsgtk_theme_image_default(i, s); > + return theme; > +} > +
> Index: gtk/gtk_theme.h > =================================================================== > --- /dev/null 2009-04-16 19:17:07.000000000 +0100 > +++ gtk/gtk_theme.h 2009-07-10 12:49:37.000000000 +0100 > @@ -0,0 +1,39 @@ > +/* > + * Copyright 2009 Mark Benjamin <[email protected]> > + * > + * This file is part of NetSurf, http://www.netsurf-browser.org/ > + * > + * NetSurf is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; version 2 of the License. > + * > + * NetSurf is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef _NETSURF_GTK_THEME_H_ > +#define _NETSURF_GTK_THEME_H_ > + > +#include <gtk/gtk.h> > +#include "gtk/gtk_scaffolding.h" > + > +extern char *current_theme_name; > + > +struct nsgtk_theme { > + GtkImage *image[PLACEHOLDER_BUTTON]; > + GtkImage *searchimage[3]; /* back, forward, close */ > + /* apng throbber element */ > +}; > + > +struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s); > +void nsgtk_theme_add(const char *themename); > +void nsgtk_theme_init(void); > +void nsgtk_theme_prepare(void); > +void nsgtk_theme_implement(struct gtk_scaffolding *g); > + > +#endif > Index: gtk/res/themes > the new folder includes one sample theme I called 'gtk+' that is the most up-to-date gtk images retrieved from the website > =================================================================== > Index: gtk/res/themes/gtk+ > =================================================================== > Index: gtk/res/themes/gtk+/print.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/print.png differ > Index: gtk/res/themes/gtk+/closetab.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/closetab.png differ > Index: gtk/res/themes/gtk+/zoomnormal.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/zoomnormal.png differ > Index: gtk/res/themes/gtk+/closewindow.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/closewindow.png differ > Index: gtk/res/themes/gtk+/printpreview.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/printpreview.png differ > Index: gtk/res/themes/gtk+/zoomminus.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/zoomminus.png differ > Index: gtk/res/themes/gtk+/back.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/back.png differ > Index: gtk/res/themes/gtk+/preferences.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/preferences.png differ > Index: gtk/res/themes/gtk+/history.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/history.png differ > Index: gtk/res/themes/gtk+/openfile.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/openfile.png differ > Index: gtk/res/themes/gtk+/delete.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/delete.png differ > Index: gtk/res/themes/gtk+/fullscreen.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/fullscreen.png differ > Index: gtk/res/themes/gtk+/forward.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/forward.png differ > Index: gtk/res/themes/gtk+/reload.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/reload.png differ > Index: gtk/res/themes/gtk+/helpcontents.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/helpcontents.png differ > Index: gtk/res/themes/gtk+/info > this file, as well as the usual blurb, contains instructions for theming > =================================================================== > --- /dev/null 2009-04-16 19:17:07.000000000 +0100 > +++ gtk/res/themes/gtk+/info 2009-07-10 12:49:47.000000000 +0100 > @@ -0,0 +1,81 @@ > +This file is part of NetSurf, http://www.netsurf-browser.org/ > + > +The images in this theme folder 'gtk+' are from the gtk stock image set > +http://library.gnome.org/devel/gtk/unstable/gtk-Stock-Items.html > + > +the image history.png is [for what it's worth!] Copyright 2009 Mark Benjamin > +<[email protected]> > + > +NetSurf is free software; you can redistribute it and/or modify > +it under the terms of the GNU General Public License as published by > +the Free Software Foundation; version 2 of the License. > + > +NetSurf is distributed in the hope that it will be useful, > +but WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +GNU General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with this program. If not, see <http://www.gnu.org/licenses/>. > + > +*** Instructions for theming *** > + > +to create a theme, make a folder, whose name is the name of the theme; > +put in the folder, a set of png images for the toolbuttons; > +the names of the images should be a subset of the list > + > + back.png, > + history.png, > + forward.png, > + stop.png, > + reload.png, > + home.png, > + newwindow.png, > + newtab.png, > + openfile.png, > + closetab.png, > + closewindow.png, > + savepage.png, > + pdf.png, > + plaintext.png, > + drawfile.png, > + postscript.png, > + printpreview.png, > + print.png, > + quit.png, > + cut.png, > + copy.png, > + paste.png, > + delete.png, > + selectall.png, > + find.png, > + preferences.png, > + zoomplus.png, > + zoomminus.png, > + zoomnormal.png, > + fullscreen.png, > + viewsource.png, > + downloads.png, > + savewindowsize.png, > + toggledebugging.png, > + saveboxtree.png, > + savedomtree.png, > + localhistory.png, > + globalhistory.png, > + addbookmarks.png, > + showbookmarks.png, > + openlocation.png, > + nexttab.png, > + prevtab.png, > + contents.png, > + guide.png, > + info.png, > + about.png, > + searchback.png, > + searchforward.png, > + searchclose.png > + > +for local theming, the folder may be placed directly [as a subfolder] into > the netsurf/gtk/res/themes folder; then 'add theme' from the > preferences->advanced tab; > + > +for downloadable themes, compile netsurf/utils/container.c according to the > instructions in the header of that file; make a netsurf container of the > folder, serve it as content-type "application/x-netsurf-theme"; browse to it > in NetSurf, then NetSurf should automatically install it > + > Index: gtk/res/themes/gtk+/selectall.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/selectall.png differ > Index: gtk/res/themes/gtk+/copy.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/copy.png differ > Index: gtk/res/themes/gtk+/paste.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/paste.png differ > Index: gtk/res/themes/gtk+/newtab.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/newtab.png differ > Index: gtk/res/themes/gtk+/newwindow.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/newwindow.png differ > Index: gtk/res/themes/gtk+/quit.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/quit.png differ > Index: gtk/res/themes/gtk+/helpabout.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/helpabout.png differ > Index: gtk/res/themes/gtk+/stop.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/stop.png differ > Index: gtk/res/themes/gtk+/home.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/home.png differ > Index: gtk/res/themes/gtk+/zoomplus.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/zoomplus.png differ > Index: gtk/res/themes/gtk+/cut.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/cut.png differ > Index: gtk/res/themes/gtk+/savepage.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/savepage.png differ > Index: gtk/res/themes/gtk+/viewsource.png > =================================================================== > Binary files /dev/null and gtk/res/themes/gtk+/viewsource.png differ > Index: gtk/res/themes/Alpha.png > =================================================================== > Binary files /dev/null and gtk/res/themes/Alpha.png differ > Index: gtk/res/default.ico > =================================================================== > Binary files /dev/null and gtk/res/default.ico differ > Index: gtk/res/themelist > =================================================================== > --- /dev/null 2009-04-16 19:17:07.000000000 +0100 > +++ gtk/res/themelist 2009-07-10 12:49:47.000000000 +0100 > @@ -0,0 +1,2 @@ > +gtk default theme > +gtk+ > Index: Docs/Doxyfile > =================================================================== > --- Docs/Doxyfile (revision 8438) > +++ Docs/Doxyfile (working copy) > @@ -896,6 +896,8 @@ > > PREDEFINED = riscos CSS_INTERNALS WITH_ARTWORKS WITH_BMP > WITH_DRAW WITH_DRAW_EXPORT WITH_GIF WITH_JPEG WITH_MMAP WITH_MNG > WITH_NSSPRITE WITH_NS_SVG WITH_PLUGIN WITH_RSVG WITH_SAVE_COMPLETE > WITH_SPRITE WITH_THEME_INSTALL WITH_PDF_EXPORT > > +PREDEFINED = gtk WITH_THEME_INSTALL > + > # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then > # this tag can be used to specify a list of macro names that should be > expanded. > # The macro definition that is found in the sources will be used. > Index: utils/config.h > =================================================================== > --- utils/config.h (revision 8438) > +++ utils/config.h (working copy) > @@ -55,6 +55,11 @@ > #define WITH_MMAP > #endif > > +#if defined(gtk) > + #define WITH_THEME_INSTALL > +#endif > + > + > /* Configuration sanity checks: */ > #if defined(WITH_NS_SVG) && defined(WITH_RSVG) > #error Cannot build WITH_NS_SVG and WITH_RSVG both enabled > Index: utils/container.c > debugging of container code; added detail to description of how to put a theme into a container; added 'gui-style' function to unwrap container > =================================================================== > --- utils/container.c (revision 8438) > +++ utils/container.c (working copy) > @@ -19,26 +19,33 @@ > /* To build a stand-alone command-line utility to create and dismantal > * these theme files, build this thusly: > * > - * gcc -I../../ -DNSTHEME -o themetool container.c > + * gcc -I../ -DNSTHEME -o themetool container.c > * > + * then for instance to create a theme file called mythemefilename > + * ./themetool --verbose --create -n"My theme name" mythemefilename\ > + * --author "Myname" /path/to/directory/containing/theme/files/ > */ > > /** \file > * Container format handling for themes etc. */ > > +#include <limits.h> > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > #include <stdbool.h> > +#include <sys/stat.h> > #include <arpa/inet.h> > +#include "utils/config.h" > #include "utils/container.h" > -#include "utils/config.h" > +#include "utils/log.h" > +#include "utils/utils.h" > #ifdef WITH_MMAP > #include <sys/mman.h> > #endif > > struct container_dirent { > - unsigned char filename[16]; > + unsigned char filename[64]; > u_int32_t startoffset; > u_int32_t len; > u_int32_t flags1; > @@ -85,7 +92,7 @@ > sizeof(struct container_dirent)); > > strncpy((char *)ctx->directory[ctx->entries - 1].filename, > - (char *)entryname, 16); > + (char *)entryname, 64); > ctx->directory[ctx->entries - 1].startoffset = offset; > ctx->directory[ctx->entries - 1].len = length; > ctx->directory[ctx->entries - 1].flags1 = 0; > @@ -94,13 +101,14 @@ > > struct container_ctx *container_open(const char *filename) > { > + size_t val; > struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1); > > ctx->fh = fopen(filename, "rb"); > > if (ctx->fh == NULL) { > free(ctx); > - return NULL; > + return NULL; > } > > /* we don't actually load any of the data (including directory) > @@ -109,16 +117,26 @@ > */ > ctx->processed = false; > > - fread(&ctx->header.magic, 4, 1, ctx->fh); > + val = fread(&ctx->header.magic, 4, 1, ctx->fh); > + if (val == 0) > + LOG(("empty read magic")); > ctx->header.magic = ntohl(ctx->header.magic); > > - fread(&ctx->header.parser, 4, 1, ctx->fh); > + val = fread(&ctx->header.parser, 4, 1, ctx->fh); > + if (val == 0) > + LOG(("empty read parser")); > ctx->header.parser = ntohl(ctx->header.parser); > > - fread(ctx->header.name, 32, 1, ctx->fh); > - fread(ctx->header.author, 64, 1, ctx->fh); > + val = fread(ctx->header.name, 32, 1, ctx->fh); > + if (val == 0) > + LOG(("empty read name")); > + val = fread(ctx->header.author, 64, 1, ctx->fh); > + if (val == 0) > + LOG(("empty read author")); > > - fread(&ctx->header.diroffset, 4, 1, ctx->fh); > + val = fread(&ctx->header.diroffset, 4, 1, ctx->fh); > + if (val == 0) > + LOG(("empty read diroffset")); > ctx->header.diroffset = ntohl(ctx->header.diroffset); > > if (ctx->header.magic != 0x4e53544d || ctx->header.parser != 3) { > @@ -132,7 +150,8 @@ > > static void container_process(struct container_ctx *ctx) > { > - unsigned char filename[16]; > + size_t val; > + unsigned char filename[64]; > u_int32_t start, len, flags1, flags2; > > #ifdef WITH_MMAP > @@ -141,14 +160,19 @@ > #else > ctx->data = malloc(ctx->header.diroffset); > fseek(ctx->fh, 0, SEEK_SET); > - fread(ctx->data, ctx->header.diroffset, 1, ctx->fh); > + val = fread(ctx->data, ctx->header.diroffset, 1, ctx->fh); > + if (val == 0) > + LOG(("empty read diroffset")); > #endif > fseek(ctx->fh, ctx->header.diroffset, SEEK_SET); > /* now work through the directory structure taking it apart into > * our structure */ > -#define BEREAD(x) do { fread(&(x), 4, 1, ctx->fh);(x) = ntohl((x)); } while > (0) > +#define BEREAD(x) do { val = fread(&(x), 4, 1, ctx->fh); if (val == 0)\ > + LOG(("empty read"));(x) = ntohl((x)); } while (0) > do { > - fread(filename, 16, 1, ctx->fh); > + val = fread(filename, 64, 1, ctx->fh); > + if (val == 0) > + LOG(("empty read filename")); > BEREAD(start); > BEREAD(len); > BEREAD(flags1); > @@ -164,7 +188,7 @@ > struct container_ctx *ctx, > const unsigned char *entryname) > { > - int i; > + unsigned int i; > > for (i = 1; i <= ctx->entries; i++) { > struct container_dirent *e = ctx->directory + i - 1; > @@ -227,12 +251,16 @@ > > static void container_write_dir(struct container_ctx *ctx) > { > - int i; > + size_t val; > + unsigned int i; > u_int32_t tmp; > -#define BEWRITE(x) do {tmp = htonl((x)); fwrite(&tmp, 4, 1, ctx->fh);} > while(0) > +#define BEWRITE(x) do {tmp = htonl((x)); val = fwrite(&tmp, 4, 1, ctx->fh);\ > + if (val == 0) LOG(("empty write")); } while(0) > for (i = 1; i <= ctx->entries; i++) { > struct container_dirent *e = ctx->directory + i - 1; > - fwrite(e->filename, 16, 1, ctx->fh); > + val = fwrite(e->filename, 64, 1, ctx->fh); > + if (val == 0) > + LOG(("empty write filename")); > BEWRITE(e->startoffset); > BEWRITE(e->len); > BEWRITE(e->flags1); > @@ -241,13 +269,16 @@ > #undef BEWRITE > /* empty entry signifies end of directory */ > tmp = 0; > - fwrite(&tmp, 4, 8, ctx->fh); > + val = fwrite(&tmp, 4, 8, ctx->fh); > + if (val == 0) > + LOG(("empty write end")); > } > > struct container_ctx *container_create(const char *filename, > const unsigned char *name, > const unsigned char *author) > { > + size_t val; > struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1); > > ctx->fh = fopen(filename, "wb"); > @@ -264,10 +295,18 @@ > strncpy((char *)ctx->header.name, (char *)name, 32); > strncpy((char *)ctx->header.author, (char *)author, 64); > > - fwrite("NSTM", 4, 1, ctx->fh); > - fwrite(&ctx->header.parser, 4, 1, ctx->fh); > - fwrite(ctx->header.name, 32, 1, ctx->fh); > - fwrite(ctx->header.author, 64, 1, ctx->fh); > + val = fwrite("NSTM", 4, 1, ctx->fh); > + if (val == 0) > + LOG(("empty write NSTM")); > + val = fwrite(&ctx->header.parser, 4, 1, ctx->fh); > + if (val == 0) > + LOG(("empty write parser")); > + val = fwrite(ctx->header.name, 32, 1, ctx->fh); > + if (val == 0) > + LOG(("empty write name")); > + val = fwrite(ctx->header.author, 64, 1, ctx->fh); > + if (val == 0) > + LOG(("empty write author")); > > ctx->header.diroffset = 108; > > @@ -284,14 +323,17 @@ > const unsigned char *data, > const u_int32_t datalen) > { > + size_t val; > container_add_to_dir(ctx, entryname, ftell(ctx->fh), datalen); > - fwrite(data, datalen, 1, ctx->fh); > + val = fwrite(data, datalen, 1, ctx->fh); > + if (val == 0) > + LOG(("empty write add file")); > } > > void container_close(struct container_ctx *ctx) > { > if (ctx->creating == true) { > - size_t flen, nflen; > + size_t flen, nflen, val; > > /* discover where the directory's going to go. */ > flen = container_filelen(ctx->fh); > @@ -300,7 +342,9 @@ > /* write this location to the header */ > fseek(ctx->fh, 104, SEEK_SET); > nflen = htonl(flen); > - fwrite(&nflen, 4, 1, ctx->fh); > + val = fwrite(&nflen, 4, 1, ctx->fh); > + if (val == 0) > + LOG(("empty write directory location")); > > /* seek to where the directory will be, and write it */ > fseek(ctx->fh, flen, SEEK_SET); > @@ -318,6 +362,72 @@ > free(ctx); > } > > +#ifdef WITH_THEME_INSTALL > + > +/** > + * install theme from container > + * \param themefile a file containing the containerized theme > + * \param dirbasename a directory basename including trailing path sep; the > + * full path of the theme is then a subdirectory of that > + */ > + > +char *container_extract_theme(const char *themefile, const char *dirbasename) > +{ > + struct stat statbuf; > + struct container_ctx *cctx; > + FILE *fh; > + size_t val; > + const unsigned char *e, *d; > + char *themename, *dirname; > + char path[PATH_MAX]; > + int state = 0; > + unsigned int i; > + u_int32_t flen; > + > + cctx = container_open(themefile); > + if (cctx == NULL) { > + warn_user("FileOpenError", themefile); > + return NULL; > + } > + themename = (char *)container_get_name(cctx); > + LOG(("theme name: %s", themename)); > + LOG(("theme author: %s", container_get_author(cctx))); > + > + dirname = malloc(strlen(dirbasename) + strlen(themename) + 2); > + strcpy(dirname, dirbasename); > + strcat(dirname, themename); > + if (stat(dirname, &statbuf) != -1) { > + warn_user("DirectoryError", dirname); > + container_close(cctx); > + free(dirname); > + return NULL; > + } > + mkdir(dirname, 00777); > + > + for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries; > + e = container_iterate(cctx, &state), i++) { > + LOG(("extracting %s", e)); > + snprintf(path, PATH_MAX, "%s/%s", dirname, e); > + fh = fopen(path, "wb"); > + if (fh == NULL) { > + warn_user("FileOpenError", (char *)e); > + } else { > + d = container_get(cctx, e, &flen); > + val = fwrite(d, flen, 1, fh); > + if (val == 0) > + LOG(("empty write")); > + fclose(fh); > + } > + } > + LOG(("theme container unpacked")); > + container_close(cctx); > + free(dirname); > + return themename; > + > +} > + > +#endif > + > #ifdef TEST_RIG > int main(int argc, char *argv[]) > { > @@ -355,7 +465,6 @@ > #include <getopt.h> > #include <dirent.h> > #include <errno.h> > -#include <sys/stat.h> > #include <unistd.h> > > static bool verbose = false; > @@ -403,7 +512,8 @@ > printf("theme author: %s\n", container_get_author(cctx)); > } > > - while ( (e = container_iterate(cctx, &state)) ) { > + for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries; > + e = container_iterate(cctx, &state), i++) { > if (verbose == true) > printf("extracting %s\n", e); > snprintf(path, PATH_MAX, "%s/%s", dirname, e); > @@ -450,9 +560,9 @@ > /* not the metadirs, so we want to process this. */ > if (verbose == true) > printf("adding %s\n", e->d_name); > - if (strlen(e->d_name) > 15) { > + if (strlen(e->d_name) > 63) { > fprintf(stderr, > - "warning: name truncated to 15 characters.\n"); > + "warning: name truncated to length 63.\n"); > } > > snprintf(path, PATH_MAX, "%s/%s", dirname, e->d_name); > Index: utils/container.h > =================================================================== > --- utils/container.h (revision 8438) > +++ utils/container.h (working copy) > @@ -47,4 +47,7 @@ > /* common interface */ > void container_close(struct container_ctx *ctx); > > +#ifdef WITH_THEME_INSTALL > +char *container_extract_theme(const char *themefile, const char > *dirbasename); > +#endif > #endif /* __CONTAINER_H__ */ -- Mark http://www.halloit.com Key ID 046B65CF
