📄 st-category-store.c
字号:
/* * Copyright (c) 2004 Jean-Yves Lefort * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */#include <string.h>#include "sg-util.h"#include "sgtk-util.h"#include "st-category-store.h"#include "st-stock.h"#include "st-handler.h"/*** type definitions ********************************************************/enum { CATEGORY_APPENDED, LAST_SIGNAL};struct _STCategoryStorePrivate{ GNode *stock_categories;};typedef struct{ STCategoryStore *store; gboolean has_selection; STCategoryBag *main_category;} MergeInfo;typedef struct{ STCategoryStoreForeachCallback cb; gpointer data;} ForeachInfo;/*** variable declarations ***************************************************/static unsigned int category_store_signals[LAST_SIGNAL] = { 0 };/*** function declarations ***************************************************/static void st_category_store_class_init (STCategoryStoreClass *class);static void st_category_store_init (STCategoryStore *store);static void st_category_store_update_icon (STCategoryStore *store, STCategoryBag *bag);static void st_category_store_weak_notify (gpointer data, GObject *former_store);static void st_category_store_category_weak_notify (gpointer data, GObject *former_bag);static void st_category_store_notify_task_thread_h (GObject *object, GParamSpec *spec, gpointer user_data);static gboolean st_category_store_append_node_cb (GNode *node, gpointer data);static gboolean st_category_store_merge_node_cb (GNode *node, gpointer data);static gboolean st_category_store_merge_node_foreach_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data);static gboolean st_category_store_merge_node_stock_cb (GNode *node, gpointer data);static gboolean st_category_store_foreach_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data);/*** implementation **********************************************************/GTypest_category_store_get_type (void){ static GType category_store_type = 0; if (! category_store_type) { static const GTypeInfo category_store_info = { sizeof(STCategoryStoreClass), NULL, NULL, (GClassInitFunc) st_category_store_class_init, NULL, NULL, sizeof(STCategoryStore), 0, (GInstanceInitFunc) st_category_store_init, }; category_store_type = g_type_register_static(GTK_TYPE_TREE_STORE, "STCategoryStore", &category_store_info, 0); } return category_store_type;}static voidst_category_store_class_init (STCategoryStoreClass *class){ g_type_class_add_private(class, sizeof(STCategoryStorePrivate)); category_store_signals[CATEGORY_APPENDED] = g_signal_new("category-appended", ST_TYPE_CATEGORY_STORE, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(STCategoryStoreClass, category_appended), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, ST_TYPE_CATEGORY_BAG);}static voidst_category_store_init (STCategoryStore *store){ store->priv = G_TYPE_INSTANCE_GET_PRIVATE(store, ST_TYPE_CATEGORY_STORE, STCategoryStorePrivate);}STCategoryStore *st_category_store_new (GNode *stock_categories){ STCategoryStore *store; GType types[] = { ST_TYPE_CATEGORY_BAG, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING }; g_return_val_if_fail(stock_categories != NULL, NULL); store = g_object_new(ST_TYPE_CATEGORY_STORE, NULL); gtk_tree_store_set_column_types(GTK_TREE_STORE(store), ST_CATEGORY_STORE_N_COLUMNS, types); /* make countable (before we start appending categories) */ sgtk_tree_model_make_countable(GTK_TREE_MODEL(store)); /* we ship the store with stock categories already appended */ store->priv->stock_categories = stock_categories; st_category_store_append_node(store, store->priv->stock_categories); return store;}voidst_category_store_append (STCategoryStore *store, STCategoryBag *bag, STCategoryBag *parent){ GtkTreeIter iter; g_return_if_fail(ST_IS_CATEGORY_STORE(store)); g_return_if_fail(ST_IS_CATEGORY_BAG(bag)); gtk_tree_store_append(GTK_TREE_STORE(store), &iter, parent ? &parent->iter : NULL); bag->iter = iter; gtk_tree_store_set(GTK_TREE_STORE(store), &iter, ST_CATEGORY_STORE_COLUMN_BAG, bag, ST_CATEGORY_STORE_COLUMN_LABEL, ST_CATEGORY(bag)->label, -1); st_category_store_update_icon(store, bag); g_signal_connect(bag, "notify::task-thread", G_CALLBACK(st_category_store_notify_task_thread_h), store); g_object_weak_ref(G_OBJECT(store), st_category_store_weak_notify, bag); g_object_weak_ref(G_OBJECT(bag), st_category_store_category_weak_notify, store); g_signal_emit(store, category_store_signals[CATEGORY_APPENDED], 0, bag);}static voidst_category_store_weak_notify (gpointer data, GObject *former_store){ STCategoryBag *bag = data; g_object_weak_unref(G_OBJECT(bag), st_category_store_category_weak_notify, former_store); g_signal_handlers_disconnect_by_func(bag, st_category_store_notify_task_thread_h, former_store);}static voidst_category_store_category_weak_notify (gpointer data, GObject *former_bag){ STCategoryStore *store = data; g_object_weak_unref(G_OBJECT(store), st_category_store_weak_notify, former_bag);}static voidst_category_store_update_icon (STCategoryStore *store, STCategoryBag *bag){ STThread *task_thread; const char *stock_id_closed; const char *stock_id_open; g_return_if_fail(ST_IS_CATEGORY_STORE(store)); g_return_if_fail(ST_IS_CATEGORY_BAG(bag)); task_thread = st_category_bag_get_task_thread(bag); if (ST_CATEGORY_BAG_IS(bag, ST_CATEGORY_BAG_MAIN)) stock_id_closed = task_thread ? ST_STOCK_MAIN_CATEGORY_RUNNING : ST_STOCK_MAIN_CATEGORY; else if (ST_CATEGORY_BAG_IS(bag, ST_CATEGORY_BAG_SEARCH)) stock_id_closed = task_thread ? ST_STOCK_SEARCH_CATEGORY_RUNNING : ST_STOCK_SEARCH_CATEGORY; else stock_id_closed = task_thread ? ST_STOCK_CATEGORY_RUNNING : ST_STOCK_CATEGORY; stock_id_open = task_thread ? ST_STOCK_CATEGORY_OPEN_RUNNING : ST_STOCK_CATEGORY_OPEN; gtk_tree_store_set(GTK_TREE_STORE(store), &bag->iter, /* used when is-expander is false */ ST_CATEGORY_STORE_COLUMN_STOCK_ID, stock_id_closed, /* used when is-expander is true */ ST_CATEGORY_STORE_COLUMN_STOCK_ID_CLOSED, stock_id_closed, ST_CATEGORY_STORE_COLUMN_STOCK_ID_OPEN, stock_id_open, -1);}static voidst_category_store_notify_task_thread_h (GObject *object, GParamSpec *spec, gpointer user_data){ STCategoryBag *bag = ST_CATEGORY_BAG(object); STCategoryStore *store = user_data; st_category_store_update_icon(store, bag);}voidst_category_store_append_node (STCategoryStore *store, GNode *node){ g_return_if_fail(ST_IS_CATEGORY_STORE(store)); g_return_if_fail(node != NULL); g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, st_category_store_append_node_cb, store);}static gbooleanst_category_store_append_node_cb (GNode *node, gpointer data){ if (node->data) { STCategoryStore *store = data; STCategoryBag *bag = node->data; STCategoryBag *parent = node->parent && node->parent->data ? node->parent->data : NULL; st_category_store_append(store, bag, parent); } return FALSE; /* continue */}/* do NOT use while a reload of the same tab is in progress */voidst_category_store_merge_node (STCategoryStore *store, GNode *node){ MergeInfo info = { store, FALSE, NULL }; g_return_if_fail(ST_IS_CATEGORY_STORE(store)); g_return_if_fail(node != NULL); g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, st_category_store_merge_node_cb, &info); if (! info.has_selection) { g_node_traverse(store->priv->stock_categories, G_PRE_ORDER, G_TRAVERSE_ALL, -1, st_category_store_merge_node_stock_cb, &info); if (! info.has_selection) st_category_bag_select(info.main_category); } st_category_store_clear(store); st_category_store_append_node(store, node);}static gbooleanst_category_store_merge_node_cb (GNode *node, gpointer data){ if (node->data) { MergeInfo *info = data; gtk_tree_model_foreach(GTK_TREE_MODEL(info->store), st_category_store_merge_node_foreach_cb, &node->data); if (ST_CATEGORY_BAG_IS_SELECTED((STCategoryBag *) node->data)) info->has_selection = TRUE; } return FALSE; /* continue */}static gbooleanst_category_store_merge_node_foreach_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data){ STCategoryBag **bag = data; STCategoryBag *this_bag; gboolean status; gtk_tree_model_get(model, iter, ST_CATEGORY_STORE_COLUMN_BAG, &this_bag, -1); if (! ST_CATEGORY_BAG_IS_STOCK(this_bag) && ST_CATEGORY_BAG_IS(this_bag, ST_CATEGORY(*bag)->name)) { st_handler_event_category_free(this_bag->handler, this_bag->category); this_bag->category = ST_CATEGORY(*bag); ST_CATEGORY(*bag) = NULL; g_object_unref(*bag); *bag = this_bag; return TRUE; /* stop */ } else { g_object_unref(this_bag); return FALSE; /* continue */ } return status;}static gbooleanst_category_store_merge_node_stock_cb (GNode *node, gpointer data){ MergeInfo *info = data; if (node->data) { STCategoryBag *bag = node->data; if (ST_CATEGORY_BAG_IS_SELECTED(bag)) info->has_selection = TRUE; else if (ST_CATEGORY_BAG_IS(bag, ST_CATEGORY_BAG_MAIN)) info->main_category = bag; } return info->has_selection;}voidst_category_store_foreach (STCategoryStore *store, STCategoryStoreForeachCallback cb, gpointer data){ ForeachInfo info = { cb, data }; g_return_if_fail(ST_IS_CATEGORY_STORE(store)); gtk_tree_model_foreach(GTK_TREE_MODEL(store), st_category_store_foreach_cb, &info);}static gbooleanst_category_store_foreach_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data){ ForeachInfo *info = data; STCategoryBag *bag; GtkTreeIter parent_iter; STCategoryBag *parent_bag = NULL; gboolean status; gtk_tree_model_get(model, iter, ST_CATEGORY_STORE_COLUMN_BAG, &bag, -1); if (gtk_tree_model_iter_parent(model, &parent_iter, iter)) gtk_tree_model_get(model, &parent_iter, ST_CATEGORY_STORE_COLUMN_BAG, &parent_bag, -1); status = info->cb(ST_CATEGORY_STORE(model), bag, parent_bag, info->data); g_object_unref(bag); if (parent_bag) g_object_unref(parent_bag); return status;}voidst_category_store_clear (STCategoryStore *store){ g_return_if_fail(ST_IS_CATEGORY_STORE(store)); gtk_tree_store_clear(GTK_TREE_STORE(store)); st_category_store_append_node(store, store->priv->stock_categories);}gbooleanst_category_store_search_equal_func (GtkTreeModel *model, int column, const char *key, GtkTreeIter *iter, gpointer search_data){ char *label; gboolean equal = FALSE; gtk_tree_model_get(model, iter, ST_CATEGORY_STORE_COLUMN_LABEL, &label, -1); equal = label && sg_utf8_strcasecontains(label, key); g_free(label); return ! equal;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -