/*************************************************************************** * icons.cpp * * Tue Aug 16 23:52:18 2005 * Copyright 2005 User * Email ****************************************************************************/ #include "icons.h" iconManager* iconMan = NULL; iconManager::iconManager() { gchar *base, *eventTheme, *smileyTheme, *statusTheme, *extendedTheme, *searchingPath; icons = NULL; eventIcons = NULL; extendedIcons = NULL; settings_getSettings()->getProperties("appearance", "EventTheme", &eventTheme, "SmileyTheme", &smileyTheme, "StatusTheme", &statusTheme, "ExtendedTheme", &extendedTheme, NULL); // load status icons base = g_strdup_printf("%sicqnd/%s/status", SHARE_DIR, statusTheme); loadStatusIcons(base, NULL, TRUE); g_free(base); // load event icons base = g_strdup_printf("%sicqnd/%s/events", SHARE_DIR, eventTheme); loadEventIcons(base, NULL); g_free(base); // load extended icons base = g_strdup_printf("%sicqnd/%s/extended", SHARE_DIR, extendedTheme); loadExtendedIcons(base, NULL); g_free(base); // load smileys base = g_strdup_printf("%sicqnd/%s/smileys", SHARE_DIR, smileyTheme); images.smileys = NULL; loadSmileys(base, &images.smileys, TRUE); g_free(base); // load the searching animation searchingPath = g_strdup_printf("%sicqnd/search.gif", SHARE_DIR); images.searchingAni = gtk_image_get_animation( GTK_IMAGE(gtk_image_new_from_file(searchingPath))); g_free(searchingPath); // append a listener for changed icons settings_getSettings()->installListener("appearance", (settingChangedFunc)this->cb_iconThemesChanged, this, TRUE); } iconManager::~iconManager() { clearStatusIcons(&icons); clearSmileys(&images.smileys, TRUE); clearIcons(&eventIcons); clearIcons(&extendedIcons); } void iconManager::clearStatusIcons(GList** list) { GList *search, *searchi; iconManagerCat *iconCat; iconManagerIcon *icon; search = *list; while(search) { iconCat = (iconManagerCat*)search->data; searchi = iconCat->icons; while(searchi) { icon = (iconManagerIcon*)searchi->data; g_object_unref(G_OBJECT(icon->pb)); g_object_unref(G_OBJECT(icon->apb)); g_free(icon); searchi = searchi->next; } g_free(iconCat->protocolID); g_list_free(iconCat->icons); g_free(iconCat); search = search->next; } if (*list) g_list_free(*list); *list = NULL; } void iconManager::clearIcons(GList** iconList) { GList *search; iconManagerIcon *icon; if (!*iconList) return; for (search = *iconList; search; search = search->next) { icon = (iconManagerIcon*)search->data; g_object_unref(icon->pb); g_free(icon); } g_list_free(*iconList); *iconList = NULL; } void iconManager::loadStatusIcons(gchar* base, GList** list, gboolean setStock) { GDir *dir, *iconDir; const gchar *currentName; gchar *iconPath, *iconName; gint i; iconManagerCat *cat; iconManagerIcon *icon; GtkIconFactory *iconFactory; GtkIconSet *stockIcon; GdkPixbuf *alphaImg; struct _iconManLoad { gchar *iconName; glong status; } iconManStatus[] = { {"away.png", BUDDY_STATUS_AWAY}, {"dnd.png", BUDDY_STATUS_DND}, {"ffc.png", BUDDY_STATUS_FFC}, {"invisible.png", BUDDY_STATUS_INV}, {"invisible_set.png", BUDDY_INV_SET}, {"na.png", BUDDY_STATUS_NA}, {"occ.png", BUDDY_STATUS_OCC}, {"offline.png", BUDDY_STATUS_OFFLINE}, {"online.png", BUDDY_STATUS_ONLINE} }; if (!list) list = &icons; // eventually clear an old structure if (*list) clearStatusIcons(list); iconFactory = NULL; if (setStock) iconFactory = gtk_icon_factory_new(); dir = g_dir_open(base, 0, NULL); while((currentName = g_dir_read_name(dir))) { iconPath = g_strdup_printf("%s/%s", base, currentName); if (g_file_test(iconPath, G_FILE_TEST_IS_DIR)) { cat = (iconManagerCat*)g_malloc0(sizeof(iconManagerCat)); cat->protocolID = g_strdup(currentName); iconDir = g_dir_open(iconPath, 0, NULL); while((currentName = g_dir_read_name(iconDir))) { for (i=0; i<9; i++) { if (!strcmp(currentName, iconManStatus[i].iconName)) { iconName = g_strdup_printf("%s/%s", iconPath, currentName); icon = (iconManagerIcon*)g_malloc0(sizeof(iconManagerIcon)); icon->identifier = iconManStatus[i].status; icon->pb = gdk_pixbuf_new_from_file(iconName, NULL); cat->icons = g_list_append(cat->icons, icon); g_free(iconName); // add a alphaed pixbuf for invisible modes alphaImg = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, gdk_pixbuf_get_bits_per_sample(icon->pb), gdk_pixbuf_get_width(icon->pb), gdk_pixbuf_get_height(icon->pb)); gdk_pixbuf_fill(alphaImg, 0x00000000); gdk_pixbuf_composite(icon->pb, alphaImg, 0, 0, gdk_pixbuf_get_width(icon->pb), gdk_pixbuf_get_height(icon->pb), 0.0f, 0.0f, 1.0f, 1.0f, GDK_INTERP_NEAREST, 128); icon->apb = alphaImg; // also add an entry to the icon factory for the menu if (setStock) { iconName = g_strdup_printf("icqnd-status-%s-%ld", cat->protocolID, iconManStatus[i].status); stockIcon = gtk_icon_set_new_from_pixbuf(icon->pb); gtk_icon_factory_add(iconFactory, iconName, stockIcon); g_free(iconName); } } } } g_dir_close(iconDir); *list = g_list_append(*list, cat); } g_free(iconPath); } if (setStock) gtk_icon_factory_add_default(iconFactory); g_dir_close(dir); } GdkPixbuf* iconManager::getStatusPixbuf(gchar* protoID, glong status, gboolean inv) { GList *search; iconManagerCat *cat; iconManagerIcon *icon; if (status == BUDDY_STATUS_INV) { status = BUDDY_STATUS_ONLINE; inv = TRUE; } search = icons; while(search) { cat = (iconManagerCat*)search->data; if (!g_ascii_strcasecmp(cat->protocolID, protoID)) break; search = search->next; } if (!search) cat = (iconManagerCat*)icons->data; search = cat->icons; while(search) { icon = (iconManagerIcon*)search->data; if (icon->identifier == status) { if (inv) return icon->apb; else return icon->pb; } search = search->next; } return NULL; } GdkPixbuf* iconManager::getUserStatusPixbuf(IMUserDaemon* user) { gchar *proto; if (!user || !user->owner || !((IMOwnerDaemon*)user->owner)->protocol) return NULL; if (user->info->pluginID == LICQ_PPID) { if (isdigit(user->info->licqID[0])) proto = "icq"; else proto = "aim"; } else proto = ((IMOwnerDaemon*)user->owner)->protocol->licqName; return getStatusPixbuf(proto, user->info->status, user->info->isInvisible); return NULL; } GdkPixbuf* iconManager::getEventPixbuf(guint ev) { GList *search; iconManagerIcon *ico; if (ev == -1) return NULL; search = eventIcons; while(search) { ico = (iconManagerIcon*)search->data; if (ico->identifier == ev) return ico->pb; search = search->next; } if (eventIcons) return ((iconManagerIcon*)eventIcons->data)->pb; else return NULL; } GdkPixbuf* iconManager::getExtendedPixbuf(guint num) { GList *search; iconManagerIcon *ico; for (search = extendedIcons; search; search = search->next) { ico = (iconManagerIcon*)search->data; if (ico->identifier == num) return ico->pb; } return NULL; } void iconManager::loadIcons(gchar *base, stockIconType* icons, GList **iconList) { GtkIconFactory *iconFactory; // the icon factory containing all icons GtkIconSet *icon; // an icon set (we use one pic for every size) GdkPixbuf *pixbuf; gchar *fileName; guint i; iconManagerIcon *newIcon; iconFactory = gtk_icon_factory_new(); // load all icons into stock i=0; while(icons[i].fileName) { fileName = g_strdup_printf("%s/%s", base, icons[i].fileName); if (g_file_test(fileName, G_FILE_TEST_EXISTS)) { pixbuf = gdk_pixbuf_new_from_file(fileName, NULL); icon = gtk_icon_set_new_from_pixbuf(pixbuf); gtk_icon_factory_add(iconFactory, icons[i].stockID, icon); if (iconList) { newIcon = (iconManagerIcon*)g_malloc0(sizeof(iconManagerIcon)); newIcon->pb = pixbuf; newIcon->identifier = icons[i].id; *iconList = g_list_append(*iconList, newIcon); } } else fprintf(stderr, "image file not found: %s\n", fileName); g_free(fileName); i++; } gtk_icon_factory_add_default(iconFactory); } void iconManager::loadEventIcons(gchar *base, GList **list) { stockIconType icons[] = { {ICON_MESSAGE, "message.png", USER_EVENT_MESSAGE}, {ICON_CHAT, "chat.png", USER_EVENT_CHAT}, {ICON_FILE, "file.png", USER_EVENT_FILE}, {ICON_URL, "url.png", USER_EVENT_URL}, {ICON_HISTORY, "history.png", 0}, {ICON_INFO, "info.png", 0}, {ICON_SMILEYS, "smiley.png", 0}, {ICON_SYSMSG, "sysmsg.png", 0}, {ICON_ARROW_CLOSED1, "arrow_c1.png", ARROW_CLOSED1}, {ICON_ARROW_CLOSED2, "arrow_c2.png", ARROW_CLOSED2}, {ICON_ARROW_OPEN1, "arrow_o1.png", ARROW_OPEN1}, {ICON_ARROW_OPEN2, "arrow_o2.png", ARROW_OPEN2}, {ICON_KEYRING, "keyring.png", 0}, {ICON_AUTH, "auth.png", 0}, {ICON_CONTACTS, "contacts.png", 0}, {ICON_SHOWOFFLINE, "showoffline.png", 0}, {ICON_GROUPS, "showgroups.png", 0}, {ICON_OPENGROUP, "arrow_c1.png", 0}, {NULL, NULL} }; if (!list) list = &eventIcons; clearIcons(list); loadIcons(base, icons, list); } void iconManager::loadExtendedIcons(gchar *base, GList** list) { stockIconType icons[] = { {ICON_BIRTHDAY, "aresponse.png", IET_ARESPONSE}, {ICON_TYPING1, "birthday.png", IET_BIRTHDAY}, {ICON_TYPING2, "cell.png", IET_CELL}, {ICON_TYPING3, "phone.png", IET_PHONE}, {ICON_TYPING3, "typing.png", IET_TYPING}, {NULL, NULL}, }; if (!list) list = &extendedIcons; clearIcons(list); loadIcons(base, icons, list); } void iconManager::loadSmileys(gchar *base, iconManagerSmiley** smileysList, gboolean buildTree) { FILE *tokensFile; gchar *tokensName; gchar *buf, *smileyName; gulong size, pos, count, smileyPos; guint i; GString *smileyToken, *imageName, *smileyCount; gboolean hasToken; GList *search, *genuineIcons; if (!smileysList) smileysList = &images.smileys; if (*smileysList) clearSmileys(smileysList, buildTree); // open the rules file tokensName = g_strdup_printf("%s/tokens.conf", base); tokensFile = fopen(tokensName, "r"); if (!tokensFile) { fprintf(stderr, "settings::loadSmileys(): Could not open smiley tokens file %s!\n", tokensName); g_free(tokensName); smileysList = NULL; smileyTree = NULL; return; } fseek(tokensFile, 0, SEEK_END); size = ftell(tokensFile); rewind(tokensFile); buf = (gchar*)g_malloc0(size); fread(buf, 1, size, tokensFile); fclose(tokensFile); g_free(tokensName); // read the file content -> create the smileys tree and load the images pos = 0; smileyCount = g_string_new(""); hasToken = FALSE; // get the smiley count while(buf[pos] != '\n') smileyCount = g_string_append_c(smileyCount, buf[pos++]); count = strtoul(smileyCount->str, NULL, 10); g_string_free(smileyCount, TRUE); // is the count valid if (!count) { fprintf(stderr, "settings::loadSmileys(): Corrupted tokens.conf file. The beginning of the file must be the smiley count.\n"); g_free(buf); fclose(tokensFile); smileysList = NULL; smileyTree = NULL; return; } smileyToken = g_string_new(""); imageName = g_string_new(""); *smileysList = (iconManagerSmiley*)g_malloc0(sizeof(iconManagerSmiley)*(count+1)); smileyPos = 0; genuineIcons = NULL; pos++; while(pos we should have a new pair if (buf[pos] == '\n') { // is the line valid? if not -> return if (!strlen(smileyToken->str) || !strlen(imageName->str)) { fprintf(stderr, "settings::loadSmileys(): Corrupted tokens.conf file. Format must be\n smileytoken[TAB]image name[RETURN]!"); g_free(tokensName); g_free(buf); g_string_free(smileyToken, TRUE); g_string_free(imageName, TRUE); fclose(tokensFile); smileysList = NULL; smileyTree = NULL; return; } smileyName = g_strdup_printf("%s/%s", base, imageName->str); (*smileysList)[smileyPos].token = g_strdup(smileyToken->str); (*smileysList)[smileyPos].icon = gdk_pixbuf_new_from_file(smileyName, NULL); search = genuineIcons; while(search) { if (!strcmp(smileyName, (gchar*)search->data)) break; search = search->next; } if (!search) genuineIcons = g_list_append(genuineIcons, g_strdup(smileyName)); (*smileysList)[smileyPos].isFirstIcon = (search == NULL); g_free(smileyName); smileyToken = g_string_assign(smileyToken, ""); imageName = g_string_assign(imageName, ""); smileyPos++; hasToken = FALSE; pos++; } if (!hasToken) smileyToken = g_string_append_c(smileyToken, buf[pos]); else if (buf[pos] != '\t' && buf[pos] != '\n') imageName = g_string_append_c(imageName, buf[pos]); pos++; } g_free(buf); g_string_free(smileyToken, TRUE); g_string_free(imageName, TRUE); g_list_foreach(genuineIcons, (GFunc)g_free, NULL); g_list_free(genuineIcons); // create a tree containing the smiley information // to avoid looking for every character of a smiley token on every text // character a tree is generated. // if we have eg. the smileys ":) :( :P", the first node only contains // one element: ":" and three branches: ")", "(", "P". This way programs // have only to look for the ":" and only THEN look for other possibilities // for this smiley. if (buildTree) { smileyTree = (smileysNode*)g_malloc0(sizeof(smileysNode)); smileyTree->summary = g_string_new(""); for (i=0;isimilies = g_list_append(smileyTree->similies, GUINT_TO_POINTER(i)); // create the smileys tree createSmileyTree(smileyTree, 0); } } void iconManager::clearSmileys(iconManagerSmiley** smileysList, gboolean clearTree) { guint i; if (!*smileysList) return; // clear the images i = 0; while((*smileysList)[i].token) { g_free((*smileysList)[i].token); g_object_unref(G_OBJECT((*smileysList)[i].icon)); i++; } g_free(*smileysList); *smileysList = NULL; if (clearTree) { // clear the smileys tree g_list_free(clearSmileysTree(smileyTree)); smileyTree = NULL; } } GList* iconManager::clearSmileysTree(smileysNode *node, GList* deletedChildren) { GList *children; // check whether this child was already deleted for (children = deletedChildren; children; children = children->next) if (children->data == node) return deletedChildren; if (node && node->children) { deletedChildren = g_list_append(deletedChildren, node); children = node->children; while(children) { deletedChildren = clearSmileysTree((smileysNode*)children->data, deletedChildren); children = children->next; } g_list_free(node->similies); g_list_free(node->children); g_string_free(node->summary, TRUE); g_free(node); } return deletedChildren; } void iconManager::createSmileyTree(smileysNode *node, guchar pos) { smileysNode *node2; GList *search, *search2; guint index; search = node->similies; while(search) { index = GPOINTER_TO_UINT(search->data); if (images.smileys[index].token && strlen(images.smileys[index].token)>pos) { search2 = node->children; while(search2) { node2 = (smileysNode*)search2->data; if (node2->character == images.smileys[index].token[pos]) break; search2 = search2->next; } if (!search2) { node2 = (smileysNode*)g_malloc0(sizeof(smileysNode)); node2->summary = g_string_new(""); node2->character = images.smileys[index].token[pos]; if (pos+1 == (guchar)strlen(images.smileys[index].token)) node2->smileysIndex = index; else node2->smileysIndex = -1; g_string_append_c(node->summary, images.smileys[index].token[pos]); node->children = g_list_append(node->children, node2); node2->similies = g_list_append(node2->similies, GUINT_TO_POINTER(index)); } else node2->similies = g_list_append(node2->similies, GUINT_TO_POINTER(index)); } search = search->next; } search = node->children; while(search) { node2 = (smileysNode*)search->data; createSmileyTree(node2, pos+1); search = search->next; } // add cross references so the user doesn't need to type a "nose" when typing a smiley search = node->children; while(search) { node2 = (smileysNode*)search->data; if (node2->character == '-') node->children = g_list_concat(node->children, g_list_copy(node2->children)); // createSmileyTree(node2, pos+1); search = search->next; } } void iconManager::cb_iconThemesChanged(gchar* sname, GList *values, iconManager* self) { settingsProperty *prop; gchar *base; while(values) { prop = (settingsProperty*)values->data; if (!strcmp(prop->name, "EventTheme")) { base = g_strdup_printf("%sicqnd/%s/events", SHARE_DIR, g_value_get_string(prop->val)); self->loadEventIcons(base, NULL); g_free(base); } else if (!strcmp(prop->name, "SmileyTheme")) { base = g_strdup_printf("%sicqnd/%s/smileys", SHARE_DIR, g_value_get_string(prop->val)); self->loadSmileys(base, &self->images.smileys, TRUE); g_free(base); } else if (!strcmp(prop->name, "StatusTheme")) { base = g_strdup_printf("%sicqnd/%s/status", SHARE_DIR, g_value_get_string(prop->val)); self->loadStatusIcons(base, NULL, TRUE); g_free(base); } else if (!strcmp(prop->name, "ExtendedTheme")) { base = g_strdup_printf("%sicqnd/%s/extended", SHARE_DIR, g_value_get_string(prop->val)); self->loadExtendedIcons(base, NULL); g_free(base); } values = values->next; } } iconManager* i_getIcons() { if (!iconMan) iconMan = new iconManager(); return iconMan; }