|
Crossfire Client, Trunk
R18666
|
00001 const char * const rcsid_gtk2_inventory_c = 00002 "$Id: inventory.c 18546 2012-12-01 10:07:28Z ryo_saeba $"; 00003 /* 00004 Crossfire client, a client program for the crossfire program. 00005 00006 Copyright (C) 2005-2007 Mark Wedel & Crossfire Development Team 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 00013 This program is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program; if not, write to the Free Software 00020 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00021 00022 The author can be reached via e-mail to crossfire@metalforge.org 00023 */ 00024 00030 #ifdef HAVE_CONFIG_H 00031 # include <config.h> 00032 #endif 00033 00034 #include <gtk/gtk.h> 00035 #include <glade/glade.h> 00036 00037 #include "client.h" 00038 00039 #include "main.h" 00040 #include "image.h" 00041 #include "gtk2proto.h" 00042 00043 #include "../../pixmaps/all.xpm" 00044 #include "../../pixmaps/coin.xpm" 00045 #include "../../pixmaps/hand.xpm" 00046 #include "../../pixmaps/hand2.xpm" 00047 #include "../../pixmaps/lock.xpm" 00048 #include "../../pixmaps/mag.xpm" 00049 #include "../../pixmaps/nonmag.xpm" 00050 #include "../../pixmaps/skull.xpm" 00051 #include "../../pixmaps/unlock.xpm" 00052 #include "../../pixmaps/unidentified.xpm" 00053 00054 GtkWidget *inv_notebook; 00055 GtkWidget *treeview_look; 00056 GtkWidget *encumbrance_current; 00057 GtkWidget *encumbrance_max; 00058 GtkWidget *inv_table; 00059 GtkTreeStore *store_look; 00060 static double weight_limit; 00061 static GtkTooltips *inv_table_tooltips; 00062 00063 /* 00064 * Hopefully, large enough. Trying to do this with malloc gets more 00065 * complicated because position of elements within the array are important, so 00066 * a simple realloc won't work. 00067 */ 00068 #define MAX_INV_COLUMNS 20 00069 #define MAX_INV_ROWS 100 00070 GtkWidget *inv_table_children[MAX_INV_ROWS][MAX_INV_COLUMNS]; 00071 00072 /* Different styles we recognize */ 00073 enum Styles { 00074 Style_Magical=0, Style_Cursed, Style_Unpaid, Style_Locked, Style_Applied, Style_Last 00075 }; 00076 00077 /* The name of these styles in the rc file */ 00078 static const char *Style_Names[Style_Last] = { 00079 "inv_magical", "inv_cursed", "inv_unpaid", "inv_locked", "inv_applied" 00080 }; 00081 00082 /* Actual styles as loaded. May be null if no style found. */ 00083 static GtkStyle *inv_styles[Style_Last]; 00084 00085 /* 00086 * The basic idea of the NoteBook_Info structure is to hold everything we need 00087 * to know about the different inventory notebooks in a module fashion - 00088 * instead of hardcoding values, they can be held in the array. 00089 */ 00090 #define NUM_INV_LISTS 11 00091 #define INV_SHOW_ITEM 0x1 00092 #define INV_SHOW_COLOR 0x2 00093 00094 enum { 00095 INV_TREE, 00096 INV_TABLE 00097 }; 00098 00099 static int num_inv_notebook_pages=0; 00100 00101 typedef struct { 00102 const char *name; 00103 const char *tooltip; 00104 const char *const *xpm; 00105 int(*show_func) (item *it); 00109 int type; 00114 GtkWidget *treeview; 00115 GtkTreeStore *treestore; 00116 } Notebook_Info; 00117 00118 static int show_all(item *it) { return INV_SHOW_ITEM | INV_SHOW_COLOR; } 00119 static int show_applied(item *it) { return (it->applied?INV_SHOW_ITEM:0); } 00120 static int show_unapplied(item *it) { return (it->applied?0:INV_SHOW_ITEM); } 00121 static int show_unpaid(item *it) { return (it->unpaid?INV_SHOW_ITEM:0); } 00122 static int show_cursed(item *it) { return ((it->cursed | it->damned)?INV_SHOW_ITEM:0); } 00123 static int show_magical(item *it) { return (it->magical?INV_SHOW_ITEM:0); } 00124 static int show_nonmagical(item *it){ return (it->magical?0:INV_SHOW_ITEM); } 00125 static int show_locked(item *it) { return (it->locked?(INV_SHOW_ITEM|INV_SHOW_COLOR):0); } 00126 static int show_unlocked(item *it) { return (it->locked?0:(INV_SHOW_ITEM|INV_SHOW_COLOR)); } 00127 static int show_unidentified(item *it) { return ((it->flagsval&F_UNIDENTIFIED)?INV_SHOW_ITEM:0); } 00128 00129 Notebook_Info inv_notebooks[NUM_INV_LISTS] = { 00130 {"all", "All Items", all_xpm, show_all, INV_TREE}, 00131 {"applied", "Applied Items", hand_xpm, show_applied, INV_TREE}, 00132 {"unapplied", "Unapplied Items", hand2_xpm, show_unapplied, INV_TREE}, 00133 {"unpaid", "Unpaid items", coin_xpm, show_unpaid, INV_TREE}, 00134 {"cursed", "Cursed items", skull_xpm, show_cursed, INV_TREE}, 00135 {"magical", "Magical items", mag_xpm, show_magical, INV_TREE}, 00136 {"nonmagical", "Nonmagical items", nonmag_xpm, show_nonmagical, INV_TREE}, 00137 {"locked", "Inventory locked items", lock_xpm, show_locked, INV_TREE}, 00138 {"unlocked", "Inventory unlocked items",unlock_xpm, show_unlocked, INV_TREE}, 00139 {"unidentified", "Inventory unidentified items", unidentified_xpm, show_unidentified, INV_TREE}, 00140 {"icons", "Quick icon view", NULL, show_all, INV_TABLE} 00141 }; 00142 00143 enum { 00144 LIST_NONE, LIST_ICON, LIST_NAME, LIST_WEIGHT, LIST_OBJECT, LIST_BACKGROUND, LIST_TYPE, 00145 LIST_BASENAME, LIST_FOREGROUND, LIST_FONT, LIST_NUM_COLUMNS 00146 }; 00147 00148 #define ITEM_INVENTORY 0x1 00149 #define ITEM_GROUND 0x2 00150 #define ITEM_IN_CONTAINER 0x4 00151 00162 static int get_item_env(item *it) 00163 { 00164 if (it->env == cpl.ob) return ITEM_INVENTORY; 00165 if (it->env == cpl.below) return ITEM_GROUND; 00166 if (it->env == NULL) return 0; 00167 return (ITEM_IN_CONTAINER | get_item_env(it->env)); 00168 } 00169 00175 static void list_item_action(GdkEventButton *event, item *tmp) 00176 { 00177 int env; 00178 00179 /* We need to know where this item is in fact is */ 00180 env = get_item_env(tmp); 00181 00182 /* It'd sure be nice if these weren't hardcoded values for button and 00183 * shift presses. 00184 */ 00185 if (event->button == 1) { 00186 if (event->state & GDK_SHIFT_MASK) 00187 toggle_locked(tmp); 00188 else 00189 client_send_examine (tmp->tag); 00190 } 00191 else if (event->button == 2) { 00192 if (event->state & GDK_SHIFT_MASK) 00193 send_mark_obj(tmp); 00194 else 00195 client_send_apply (tmp->tag); 00196 } 00197 else if (event->button == 3) { 00198 if (tmp->locked) { 00199 draw_ext_info(NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_NOTICE, 00200 "This item is locked. To drop it, first unlock by shift+leftclicking on it."); 00201 } else { 00202 uint32 dest; 00203 00204 cpl.count = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spinbutton_count)); 00205 /* 00206 * Figure out where to move the item to. If it is on the ground, 00207 * it is moving to the players inventory. If it is in a container, 00208 * it is also moving to players inventory. If it is in the players 00209 * inventory (not a container) and the player has an open container 00210 * in his inventory, move the object to the container (not ground). 00211 * Otherwise, it is moving to the ground (dest=0). Have to look at 00212 * the item environment, because what list is no longer accurate. 00213 */ 00214 if (env & (ITEM_GROUND | ITEM_IN_CONTAINER)) 00215 dest = cpl.ob->tag; 00216 else if (env == ITEM_INVENTORY && cpl.container && 00217 (get_item_env(cpl.container) == ITEM_INVENTORY || 00218 get_item_env(cpl.container) == ITEM_GROUND)) { 00219 dest = cpl.container->tag; 00220 } else 00221 dest = 0; 00222 00223 client_send_move (dest, tmp->tag, cpl.count); 00224 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton_count),0.0); 00225 cpl.count=0; 00226 } 00227 } 00228 } 00229 00244 gboolean list_selection_func ( 00245 GtkTreeSelection *selection, 00246 GtkTreeModel *model, 00247 GtkTreePath *path, 00248 gboolean path_currently_selected, 00249 gpointer userdata) 00250 { 00251 GtkTreeIter iter; 00252 GdkEventButton *event; 00253 00254 /* Get the current event so we can know if shift is pressed */ 00255 event = (GdkEventButton*)gtk_get_current_event(); 00256 if (!event) { 00257 LOG(LOG_ERROR,"inventory.c::list_selection_func", "Unable to get event structure\n"); 00258 return FALSE; 00259 } 00260 00261 if (gtk_tree_model_get_iter(model, &iter, path)) { 00262 item *tmp; 00263 00264 gtk_tree_model_get(model, &iter, LIST_OBJECT, &tmp, -1); 00265 00266 if (!tmp) { 00267 LOG(LOG_ERROR,"inventory.c::list_selection_func", "Unable to get item structure\n"); 00268 return FALSE; 00269 } 00270 list_item_action(event, tmp); 00271 } 00272 /* 00273 * Don't want the row toggled - our code above handles what we need to do, 00274 * so return false. 00275 */ 00276 return FALSE; 00277 } 00278 00288 void 00289 list_row_collapse (GtkTreeView *treeview, 00290 GtkTreeIter *iter, 00291 GtkTreePath *path, 00292 gpointer user_data) 00293 { 00294 GtkTreeModel *model; 00295 item *tmp; 00296 00297 model = gtk_tree_view_get_model(treeview); 00298 00299 gtk_tree_model_get(GTK_TREE_MODEL(model), iter, LIST_OBJECT, &tmp, -1); 00300 client_send_apply (tmp->tag); 00301 } 00302 00307 static void setup_list_columns(GtkWidget *treeview) 00308 { 00309 GtkCellRenderer *renderer; 00310 GtkTreeViewColumn *column; 00311 GtkTreeSelection *selection; 00312 00313 #if 0 00314 /* 00315 * This is a hack to hide the expander column. We do this because access 00316 * via containers need to be handled by the apply/unapply mechanism - 00317 * otherwise, I think it will be confusing - people 'closing' the container 00318 * with the expander arrow and still having things go into/out of the 00319 * container. Unfortunat 00320 */ 00321 renderer = gtk_cell_renderer_text_new (); 00322 column = gtk_tree_view_column_new_with_attributes ("", renderer, 00323 "text", LIST_NONE, 00324 NULL); 00325 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 00326 gtk_tree_view_column_set_visible(column, FALSE); 00327 gtk_tree_view_set_expander_column(GTK_TREE_VIEW (treeview), column); 00328 #endif 00329 00330 renderer = gtk_cell_renderer_pixbuf_new (); 00331 /* 00332 * Setting the xalign to 0.0 IMO makes the display better. Gtk 00333 * automatically resizes the column to make space based on image size, 00334 * however, it isn't really aggressive on shrinking it. IMO, it looks 00335 * better for the image to always be at the far left - without this 00336 * alignment, the image is centered which IMO doesn't always look good. 00337 */ 00338 g_object_set (G_OBJECT (renderer), "xalign", 0.0, 00339 NULL); 00340 column = gtk_tree_view_column_new_with_attributes ("?", renderer, 00341 "pixbuf", LIST_ICON, 00342 NULL); 00343 00344 /* gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);*/ 00345 gtk_tree_view_column_set_min_width(column, image_size); 00346 gtk_tree_view_column_set_sort_column_id(column, LIST_TYPE); 00347 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 00348 00349 renderer = gtk_cell_renderer_text_new (); 00350 column = gtk_tree_view_column_new_with_attributes ("Name", renderer, 00351 "text", LIST_NAME, 00352 NULL); 00353 gtk_tree_view_column_set_expand(column, TRUE); 00354 gtk_tree_view_column_set_sort_column_id(column, LIST_BASENAME); 00355 00356 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 00357 gtk_tree_view_column_add_attribute(column, renderer, "background-gdk", LIST_BACKGROUND); 00358 gtk_tree_view_column_add_attribute(column, renderer, "foreground-gdk", LIST_FOREGROUND); 00359 gtk_tree_view_column_add_attribute(column, renderer, "font-desc", LIST_FONT); 00360 gtk_tree_view_set_expander_column(GTK_TREE_VIEW (treeview), column); 00361 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); 00362 00363 renderer = gtk_cell_renderer_text_new (); 00364 column = gtk_tree_view_column_new_with_attributes ("Weight", renderer, 00365 "text", LIST_WEIGHT, 00366 NULL); 00367 /* 00368 * At 50, the title was always truncated on some systems. 64 is the 00369 * minimum on those systems for it to be possible to avoid truncation at 00370 * all. Truncating the title looks cheesy, especially since heavy items 00371 * (100+) need the width of the field anyway. If weight pushed off the 00372 * edge is a problem, it would be just better to let the user resize or 00373 * find a way to allow rendering with a smaller font. 00374 */ 00375 gtk_tree_view_column_set_min_width(column, 64); 00376 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); 00377 00378 gtk_tree_view_column_set_sort_column_id(column, LIST_WEIGHT); 00379 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 00380 gtk_tree_view_column_add_attribute(column, renderer, "background-gdk", LIST_BACKGROUND); 00381 gtk_tree_view_column_add_attribute(column, renderer, "foreground-gdk", LIST_FOREGROUND); 00382 gtk_tree_view_column_add_attribute(column, renderer, "font-desc", LIST_FONT); 00383 /* 00384 * Really, we never really do selections - clicking on an object causes a 00385 * reaction right then. So grab press before the selection and just negate 00386 * the selection - that's more efficient than unselection the item after it 00387 * was selected. 00388 */ 00389 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); 00390 00391 gtk_tree_selection_set_select_function(selection, list_selection_func, NULL, NULL); 00392 } 00393 00401 void inventory_get_styles(void) 00402 { 00403 int i; 00404 GtkStyle *tmp_style; 00405 static int has_init=0; 00406 00407 for (i=0; i < Style_Last; i++) { 00408 if (has_init && inv_styles[i]) g_object_unref(inv_styles[i]); 00409 tmp_style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), NULL, Style_Names[i], 00410 G_TYPE_NONE); 00411 if (tmp_style) { 00412 inv_styles[i] = g_object_ref(tmp_style); 00413 } 00414 else { 00415 LOG(LOG_INFO, "inventory.c::inventory_get_styles", "Unable to find style for %s", 00416 Style_Names[i]); 00417 inv_styles[i] = NULL; 00418 } 00419 } 00420 has_init=1; 00421 } 00422 00428 void inventory_init(GtkWidget *window_root) 00429 { 00430 int i; 00431 GladeXML *xml_tree; 00432 00433 /*inventory_get_styles();*/ 00434 00435 xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root)); 00436 00437 inv_notebook = glade_xml_get_widget(xml_tree,"notebook_inv"); 00438 treeview_look = glade_xml_get_widget(xml_tree, "treeview_look"); 00439 encumbrance_current = glade_xml_get_widget(xml_tree,"label_stat_encumbrance_current"); 00440 encumbrance_max = glade_xml_get_widget(xml_tree,"label_stat_encumbrance_max"); 00441 inv_table = glade_xml_get_widget(xml_tree,"inv_table"); 00442 00443 g_signal_connect((gpointer) inv_notebook, "switch_page", 00444 (GCallback) on_notebook_switch_page, NULL); 00445 g_signal_connect((gpointer) inv_table, "expose_event", 00446 (GCallback) on_inv_table_expose_event, NULL); 00447 g_signal_connect((gpointer) treeview_look, "row_collapsed", 00448 (GCallback) list_row_collapse, NULL); 00449 00450 inv_table_tooltips = gtk_tooltips_new(); 00451 gtk_tooltips_enable(inv_table_tooltips); 00452 00453 memset(inv_table_children, 0, sizeof(GtkWidget *) * MAX_INV_ROWS * MAX_INV_COLUMNS); 00454 00455 store_look = gtk_tree_store_new (LIST_NUM_COLUMNS, 00456 G_TYPE_STRING, 00457 G_TYPE_OBJECT, 00458 G_TYPE_STRING, 00459 G_TYPE_STRING, 00460 G_TYPE_POINTER, 00461 GDK_TYPE_COLOR, 00462 G_TYPE_INT, 00463 G_TYPE_STRING, 00464 GDK_TYPE_COLOR, 00465 PANGO_TYPE_FONT_DESCRIPTION); 00466 00467 gtk_tree_view_set_model(GTK_TREE_VIEW(treeview_look), GTK_TREE_MODEL(store_look)); 00468 setup_list_columns(treeview_look); 00469 /* 00470 * Glade doesn't let us fully realize a treeview widget - we still need to 00471 * to do a bunch of customization just like we do for the look window 00472 * above. If we have to do all that work, might as well just put it in the 00473 * for loop below vs setting up half realized widgets within glade that we 00474 * then need to finish setting up. However, that said, we want to be able 00475 * to set up other notebooks within glade for perhaps a true list of just 00476 * icons. So we presume that any tabs that exist must already be all set 00477 * up. We prepend our tabs to the existing tab - this makes the position 00478 * of the array of noteboks correspond to actual data in the tabs. 00479 */ 00480 for (i=0; i < NUM_INV_LISTS; i++) { 00481 GtkWidget *swindow, *image; 00482 00483 if (inv_notebooks[i].type == INV_TREE) { 00484 swindow = gtk_scrolled_window_new(NULL, NULL); 00485 gtk_widget_show(swindow); 00486 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), 00487 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); 00488 image = gtk_image_new_from_pixbuf( 00489 gdk_pixbuf_new_from_xpm_data((const char**)inv_notebooks[i].xpm)); 00490 00491 if (inv_notebooks[i].tooltip) { 00492 GtkWidget *eb; 00493 00494 eb=gtk_event_box_new(); 00495 gtk_widget_show(eb); 00496 00497 gtk_container_add(GTK_CONTAINER(eb), image); 00498 gtk_widget_show(image); 00499 00500 image=eb; 00501 gtk_tooltips_set_tip(inv_table_tooltips, image, inv_notebooks[i].tooltip, NULL); 00502 } 00503 00504 gtk_notebook_insert_page(GTK_NOTEBOOK(inv_notebook), swindow, image, i); 00505 00506 inv_notebooks[i].treestore = gtk_tree_store_new (LIST_NUM_COLUMNS, 00507 G_TYPE_STRING, 00508 G_TYPE_OBJECT, 00509 G_TYPE_STRING, 00510 G_TYPE_STRING, 00511 G_TYPE_POINTER, 00512 GDK_TYPE_COLOR, 00513 G_TYPE_INT, 00514 G_TYPE_STRING, 00515 GDK_TYPE_COLOR, 00516 PANGO_TYPE_FONT_DESCRIPTION); 00517 00518 inv_notebooks[i].treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL( 00519 inv_notebooks[i].treestore)); 00520 00521 g_signal_connect ((gpointer) inv_notebooks[i].treeview, "row_collapsed", 00522 G_CALLBACK (list_row_collapse), NULL); 00523 00524 setup_list_columns(inv_notebooks[i].treeview); 00525 gtk_widget_show(inv_notebooks[i].treeview); 00526 gtk_container_add(GTK_CONTAINER(swindow), inv_notebooks[i].treeview); 00527 } 00528 } 00529 num_inv_notebook_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(inv_notebook)); 00530 00531 /* Make sure we are on the first page */ 00532 gtk_notebook_set_current_page(GTK_NOTEBOOK(inv_notebook), 0); 00533 00534 /* If all the data is set up properly, these should match */ 00535 if (num_inv_notebook_pages != NUM_INV_LISTS) { 00536 LOG(LOG_ERROR,"inventory.c::inventory_init", 00537 "num_inv_notebook_pages (%d) does not match NUM_INV_LISTS(%d)\n", 00538 num_inv_notebook_pages, NUM_INV_LISTS); 00539 } 00540 00541 } 00542 00547 void set_show_icon (const char *s) 00548 { 00549 } 00550 00555 void set_show_weight (const char *s) 00556 { 00557 } 00558 00567 void close_container(item *op) 00568 { 00569 draw_lists(); 00570 } 00571 00576 void open_container (item *op) 00577 { 00578 draw_lists(); 00579 } 00580 00585 void command_show (const char *params) 00586 { 00587 if(!params) { 00588 /* 00589 * Shouldn't need to get current page, but next_page call is not 00590 * wrapping like the docs claim it should. 00591 */ 00592 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook))==num_inv_notebook_pages) 00593 gtk_notebook_set_page(GTK_NOTEBOOK(inv_notebook), 0); 00594 else 00595 gtk_notebook_next_page(GTK_NOTEBOOK(inv_notebook)); 00596 } else { 00597 int i; 00598 char buf[MAX_BUF]; 00599 00600 for (i=0; i < NUM_INV_LISTS; i++) { 00601 if (!strncmp(params, inv_notebooks[i].name, strlen(params))) { 00602 gtk_notebook_set_page(GTK_NOTEBOOK(inv_notebook), i); 00603 return; 00604 } 00605 } 00606 snprintf(buf, sizeof(buf), "Unknown notebook page %s\n", params); 00607 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_ERROR, buf); 00608 } 00609 } 00610 00617 void set_weight_limit (uint32 wlim) 00618 { 00619 weight_limit = wlim/ 1000.0; 00620 } 00621 00627 static GtkStyle *get_row_style(item *it) 00628 { 00629 int style; 00630 00631 /* Note that this ordering is documented in the sample rc file. 00632 * it would be nice if this precedence could be more easily 00633 * setable by the end user. 00634 */ 00635 if (it->unpaid) style=Style_Unpaid; 00636 else if (it->cursed || it->damned) style=Style_Cursed; 00637 else if (it->magical) style = Style_Magical; 00638 else if (it->applied) style = Style_Applied; 00639 else if (it->locked) style = Style_Locked; 00640 else return NULL; /* No matching style */ 00641 00642 return inv_styles[style]; 00643 } 00644 00645 /*************************************************************************** 00646 * Below are the actual guts for drawing the inventory and look windows. 00647 * Some quick notes: 00648 * 1) The gtk2 widgets (treeview/treemodel) seem noticably slower than the 00649 * older clist widgets in gtk1. This is beyond the code below - just 00650 * scrolling the window, which is all done internally by gtk, is 00651 * quite slow. Seems especially bad when using the scrollwheel. 00652 * 2) documentation suggests the detaching the treemodel and re-attaching 00653 * it after insertions would be faster. The problem is that this causes 00654 * loss of positioning for the scrollbar. Eg, you eat a food in the middle 00655 * of your inventory, and then inventory resets to the top of the inventory. 00656 * 3) it'd probably be much more efficient if the code could know what changes 00657 * are taking place, instead of rebuilding the tree model each time. For 00658 * example, if the only thing that changed is the number of of the object, 00659 * we can just update the name and weight, and not rebuild the entire list. 00660 * This may be doable in the code below by getting data from the tree store 00661 * and comparing it against what we want to show - however, figuring out 00662 * insertions and removals are more difficult. 00663 */ 00664 00665 void item_event_item_deleting(item * it) {} 00666 void item_event_container_clearing(item * container) {} 00667 void item_event_item_changed(item * it) {} 00668 00682 static void add_object_to_store(item *it, GtkTreeStore *store, 00683 GtkTreeIter *new, GtkTreeIter *parent, int color) 00684 { 00685 char buf[256], buf1[256]; 00686 GdkColor *foreground=NULL, *background=NULL; 00687 PangoFontDescription *font=NULL; 00688 GtkStyle *row_style; 00689 00690 if(it->weight < 0) { 00691 strcpy (buf," "); 00692 } else { 00693 snprintf(buf, sizeof(buf), "%6.1f" ,it->nrof * it->weight); 00694 } 00695 snprintf(buf1, 255, "%s %s", it->d_name, it->flags); 00696 if (color) { 00697 row_style = get_row_style(it); 00698 if (row_style) { 00699 /* 00700 * Even if the user doesn't define these, we should still get get 00701 * defaults from the system. 00702 */ 00703 foreground = &row_style->text[GTK_STATE_NORMAL]; 00704 background = &row_style->base[GTK_STATE_NORMAL]; 00705 font = row_style->font_desc; 00706 } 00707 } 00708 00709 gtk_tree_store_append (store, new, parent); /* Acquire an iterator */ 00710 gtk_tree_store_set (store, new, 00711 LIST_ICON, (GdkPixbuf*)pixmaps[it->face]->icon_image, 00712 LIST_NAME, buf1, 00713 LIST_WEIGHT, buf, 00714 LIST_BACKGROUND, background, 00715 LIST_FOREGROUND, foreground, 00716 LIST_FONT, font, 00717 LIST_OBJECT, it, 00718 LIST_TYPE, it->type, 00719 LIST_BASENAME, it->s_name, 00720 -1); 00721 } 00722 00726 void draw_look_list(void) 00727 { 00728 item *tmp; 00729 GtkTreeIter iter; 00730 /* 00731 * List drawing is actually fairly inefficient - we only know globally if 00732 * the objects has changed, but have no idea what specific object has 00733 * changed. As such, we are forced to basicly redraw the entire list each 00734 * time this is called. 00735 */ 00736 gtk_tree_store_clear(store_look); 00737 00738 for (tmp=cpl.below->inv; tmp; tmp=tmp->next) { 00739 add_object_to_store(tmp, store_look, &iter, NULL, 1); 00740 00741 if ((cpl.container == tmp) && tmp->open) { 00742 item *tmp2; 00743 GtkTreeIter iter1; 00744 GtkTreePath *path; 00745 00746 for (tmp2 = tmp->inv; tmp2; tmp2=tmp2->next) { 00747 add_object_to_store(tmp2, store_look, &iter1, &iter, 1); 00748 } 00749 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store_look), &iter); 00750 gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview_look), path, FALSE); 00751 gtk_tree_path_free (path); 00752 } 00753 } 00754 } 00755 00762 void draw_inv_list(int tab) 00763 { 00764 item *tmp; 00765 GtkTreeIter iter; 00766 int rowflag; 00767 00768 /* 00769 * List drawing is actually fairly inefficient - we only know globally if 00770 * the objects has changed, but have no idea what specific object has 00771 * changed. As such, we are forced to basicly redraw the entire list each 00772 * time this is called. 00773 */ 00774 gtk_tree_store_clear(inv_notebooks[tab].treestore); 00775 00776 for (tmp=cpl.ob->inv; tmp; tmp=tmp->next) { 00777 rowflag = inv_notebooks[tab].show_func(tmp); 00778 if (!(rowflag & INV_SHOW_ITEM)) continue; 00779 00780 add_object_to_store(tmp, inv_notebooks[tab].treestore, &iter, NULL, rowflag & INV_SHOW_COLOR); 00781 00782 if ((cpl.container == tmp) && tmp->open) { 00783 item *tmp2; 00784 GtkTreeIter iter1; 00785 GtkTreePath *path; 00786 00787 for (tmp2 = tmp->inv; tmp2; tmp2=tmp2->next) { 00788 /* 00789 * Wonder if we really want this logic for objects in 00790 * containers? my thought is yes - being able to see all 00791 * cursed objects in the container could be quite useful. 00792 * Unfortunately, that doesn't quite work as intended, because 00793 * we will only get here if the container object is being 00794 * displayed. Since container objects can't be cursed, can't 00795 * use that as a filter. 00796 */ 00797 /* 00798 rowflag = inv_notebooks[tab].show_func(tmp2); 00799 */ 00800 if (!(rowflag & INV_SHOW_ITEM)) continue; 00801 add_object_to_store(tmp2, inv_notebooks[tab].treestore, &iter1, &iter, 00802 rowflag & INV_SHOW_COLOR); 00803 } 00804 path = gtk_tree_model_get_path(GTK_TREE_MODEL(inv_notebooks[tab].treestore), &iter); 00805 gtk_tree_view_expand_row(GTK_TREE_VIEW(inv_notebooks[tab].treeview), path, FALSE); 00806 gtk_tree_path_free (path); 00807 } 00808 } 00809 } 00810 00818 gboolean 00819 drawingarea_inventory_table_button_press_event (GtkWidget *widget, 00820 GdkEventButton *event, 00821 gpointer user_data) 00822 { 00823 list_item_action(event, (item*)user_data); 00824 return TRUE; 00825 } 00826 00834 gboolean 00835 drawingarea_inventory_table_expose_event (GtkWidget *widget, 00836 GdkEventExpose *event, 00837 gpointer user_data) 00838 { 00839 item *tmp; 00840 00841 tmp = (item*)user_data; 00842 00843 gdk_window_clear(widget->window); 00844 /* 00845 * Can get cases when switching tabs that we get an expose event before the 00846 * list is updated - if so, don't draw stuff we don't have faces for. 00847 */ 00848 if (tmp->face) 00849 gdk_draw_pixbuf(widget->window, NULL, 00850 (GdkPixbuf*)pixmaps[tmp->face]->icon_image, 00851 0, 0, 0, 0, image_size, image_size, GDK_RGB_DITHER_NONE, 0, 0); 00852 return TRUE; 00853 } 00854 00855 #define INVHELPTEXT "Left click examines the object. Middle click applies \ 00856 the object. Right click drops the object. Shift left click locks/unlocks the \ 00857 object. Shift middle click marks the object" 00858 00865 void draw_inv_table(int animate) 00866 { 00867 int x, y, rows, columns, num_items, i; 00868 static int max_drawn=0; 00869 item *tmp; 00870 char buf[256]; 00871 gulong handler; 00872 00873 num_items=0; 00874 for (tmp=cpl.ob->inv; tmp; tmp=tmp->next) 00875 num_items++; 00876 00877 columns = inv_table->allocation.width / image_size; 00878 if (columns > MAX_INV_COLUMNS) columns = MAX_INV_COLUMNS; 00879 rows = inv_table->allocation.height / image_size; 00880 00881 if (num_items > columns * rows) { 00882 rows = num_items / columns; 00883 if (num_items % columns) rows++; 00884 } 00885 if (rows > MAX_INV_ROWS) rows=MAX_INV_ROWS; 00886 00887 gtk_table_resize(GTK_TABLE(inv_table), rows, columns); 00888 00889 x=0; 00890 y=0; 00891 for (tmp=cpl.ob->inv; tmp; tmp=tmp->next) { 00892 if (inv_table_children[x][y] == NULL) { 00893 inv_table_children[x][y] = gtk_drawing_area_new(); 00894 gtk_drawing_area_size (GTK_DRAWING_AREA(inv_table_children[x][y]), 00895 image_size, image_size); 00896 00897 gtk_table_attach(GTK_TABLE(inv_table), inv_table_children[x][y], 00898 x, x+1, y, y+1, GTK_FILL, GTK_FILL, 0, 0); 00899 } 00900 if (animate) { 00901 /* This is an object with animations */ 00902 if (tmp->animation_id >0 && tmp->anim_speed) { 00903 tmp->last_anim++; 00904 00905 /* Time to change the face for this one */ 00906 if (tmp->last_anim >= tmp->anim_speed) { 00907 tmp->anim_state++; 00908 if (tmp->anim_state >= animations[tmp->animation_id].num_animations) 00909 tmp->anim_state=0; 00910 tmp->face = animations[tmp->animation_id].faces[tmp->anim_state]; 00911 tmp->last_anim=0; 00912 00913 gdk_window_clear(inv_table_children[x][y]->window); 00914 gdk_draw_pixbuf(inv_table_children[x][y]->window, NULL, 00915 (GdkPixbuf*)pixmaps[tmp->face]->icon_image, 00916 0, 0, 0, 0, image_size, image_size, GDK_RGB_DITHER_NONE, 0, 0); 00917 } 00918 } 00919 /* On animation run, so don't do any of the remaining logic */ 00920 } else { 00921 /* 00922 * Need to clear out the old signals, since the signals are 00923 * effectively stacked - you can have 6 signal handlers tied to the 00924 * same function. 00925 */ 00926 handler = g_signal_handler_find((gpointer)inv_table_children[x][y], 00927 G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 00928 G_CALLBACK (drawingarea_inventory_table_button_press_event), 00929 NULL); 00930 00931 if (handler) 00932 g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler); 00933 00934 handler = g_signal_handler_find((gpointer)inv_table_children[x][y], 00935 G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 00936 G_CALLBACK (drawingarea_inventory_table_expose_event), 00937 NULL); 00938 if (handler) 00939 g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler); 00940 /* 00941 * Not positive precisely what events are needed, but some events 00942 * beyond just the button press are necessary for the tooltips to 00943 * work. 00944 */ 00945 gtk_widget_add_events (inv_table_children[x][y], GDK_ALL_EVENTS_MASK); 00946 00947 g_signal_connect ((gpointer) inv_table_children[x][y], "button_press_event", 00948 G_CALLBACK (drawingarea_inventory_table_button_press_event), 00949 tmp); 00950 00951 g_signal_connect ((gpointer) inv_table_children[x][y], "expose_event", 00952 G_CALLBACK (drawingarea_inventory_table_expose_event), 00953 tmp); 00954 00955 gdk_window_clear(inv_table_children[x][y]->window); 00956 gdk_draw_pixbuf(inv_table_children[x][y]->window, NULL, 00957 (GdkPixbuf*)pixmaps[tmp->face]->icon_image, 00958 0, 0, 0, 0, image_size, image_size, GDK_RGB_DITHER_NONE, 0, 0); 00959 00960 gtk_widget_show(inv_table_children[x][y]); 00961 /* 00962 * Use tooltips to provide additional detail about the icons. 00963 * Looking at the code, the tooltip widget will take care of 00964 * removing the old tooltip, freeing strings, etc. 00965 */ 00966 snprintf(buf, 255, "%s %s", tmp->d_name, tmp->flags); 00967 gtk_tooltips_set_tip(inv_table_tooltips, inv_table_children[x][y], 00968 buf, INVHELPTEXT); 00969 } 00970 x++; 00971 if (x == columns) { 00972 x=0; 00973 y++; 00974 } 00975 00976 } 00977 /* Don't need to do the logic below if only doing animation run */ 00978 if (animate) return; 00979 /* 00980 * Need to disconnect the callback functions cells we did not draw. 00981 * otherwise, we get errors on objects that are drawn. 00982 */ 00983 for (i=num_items; i<=max_drawn; i++) { 00984 if (inv_table_children[x][y]) { 00985 gdk_window_clear(inv_table_children[x][y]->window); 00986 00987 handler = g_signal_handler_find((gpointer)inv_table_children[x][y], 00988 G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 00989 G_CALLBACK (drawingarea_inventory_table_button_press_event), 00990 NULL); 00991 00992 if (handler) 00993 g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler); 00994 00995 handler = g_signal_handler_find((gpointer)inv_table_children[x][y], 00996 G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 00997 G_CALLBACK (drawingarea_inventory_table_expose_event), 00998 NULL); 00999 if (handler) 01000 g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler); 01001 01002 /* Hide the widget so that the tooltips doesn't show up */ 01003 gtk_widget_hide(inv_table_children[x][y]); 01004 } 01005 x++; 01006 if (x == columns) { 01007 x=0; 01008 y++; 01009 } 01010 } 01011 max_drawn = num_items; 01012 01013 gtk_widget_show(inv_table); 01014 } 01015 01022 void draw_inv(int tab) 01023 { 01024 char buf[256]; 01025 01026 snprintf(buf, sizeof(buf), "%6.1f", cpl.ob->weight); 01027 gtk_label_set(GTK_LABEL(encumbrance_current), buf); 01028 snprintf(buf, sizeof(buf), "%6.1f", weight_limit); 01029 gtk_label_set(GTK_LABEL(encumbrance_max), buf); 01030 01031 if (inv_notebooks[tab].type == INV_TREE) 01032 draw_inv_list(tab); 01033 else if (inv_notebooks[tab].type == INV_TABLE) 01034 draw_inv_table(0); 01035 } 01036 01040 void draw_lists (void) 01041 { 01042 /* 01043 * There are some extra complications with container handling and timing. 01044 * For example, we draw the window before we get a list of the container, 01045 * and then the container contents are not drawn - this can be handled by 01046 * looking at container->inv_updated. 01047 */ 01048 if (cpl.container && cpl.container->inv_updated) { 01049 cpl.container->env->inv_updated = 1; 01050 cpl.container->inv_updated=0; 01051 } 01052 if (cpl.ob->inv_updated) { 01053 draw_inv(gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook))); 01054 cpl.ob->inv_updated=0; 01055 } 01056 if (cpl.below->inv_updated) { 01057 draw_look_list(); 01058 cpl.below->inv_updated=0; 01059 } 01060 } 01061 01074 void 01075 on_notebook_switch_page (GtkNotebook *notebook, 01076 GtkNotebookPage *page, 01077 guint page_num, 01078 gpointer user_data) 01079 { 01080 int oldpage; 01081 01082 oldpage = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); 01083 if (oldpage != page_num && inv_notebooks[oldpage].type == INV_TREE) 01084 gtk_tree_store_clear(inv_notebooks[oldpage].treestore); 01085 cpl.ob->inv_updated=1; 01086 } 01087 01095 gboolean 01096 on_inv_table_expose_event (GtkWidget *widget, 01097 GdkEventExpose *event, 01098 gpointer user_data) 01099 { 01100 draw_inv_table(0); 01101 return TRUE; 01102 } 01103 01107 void animate_inventory(void) 01108 { 01109 gboolean valid; 01110 GtkTreeIter iter; 01111 item *tmp; 01112 int page; 01113 GtkTreeStore *store; 01114 static int inv_tick=0; 01115 01116 /* 01117 * If global tick is set, then we are getting tick events from server to 01118 * keep in sync, so we don't need the logic below. 01119 */ 01120 if (!tick) { 01121 /* 01122 * The gtk client timeout is 12 times faster than that of the server so 01123 * we slow it down here. If we were really clever, we'd find what the 01124 * timeout on the server actually is, and do gettimeofday calls here to 01125 * remain very closely in sync. 01126 */ 01127 inv_tick++; 01128 if (inv_tick < 12) return; 01129 inv_tick=0; 01130 } 01131 01132 page = gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook)); 01133 01134 /* Still need to do logic for the table view. */ 01135 if (inv_notebooks[page].type == INV_TABLE) { 01136 draw_inv_table(1); 01137 return; 01138 } 01139 01140 store = inv_notebooks[page].treestore; 01141 01142 /* Get the first iter in the list */ 01143 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter); 01144 01145 while (valid) { 01146 gtk_tree_model_get (GTK_TREE_MODEL(store), &iter, 01147 LIST_OBJECT, &tmp, 01148 -1); 01149 01150 /* This is an object with animations */ 01151 if (tmp->animation_id >0 && tmp->anim_speed) { 01152 tmp->last_anim++; 01153 01154 /* Time to change the face for this one */ 01155 if (tmp->last_anim >= tmp->anim_speed) { 01156 tmp->anim_state++; 01157 if (tmp->anim_state >= animations[tmp->animation_id].num_animations) 01158 tmp->anim_state=0; 01159 tmp->face = animations[tmp->animation_id].faces[tmp->anim_state]; 01160 tmp->last_anim=0; 01161 01162 /* Update image in the tree store */ 01163 gtk_tree_store_set(store, &iter, 01164 LIST_ICON, (GdkPixbuf*)pixmaps[tmp->face]->icon_image, 01165 -1); 01166 } 01167 } 01168 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(store), &iter); 01169 } 01170 } 01171 01175 void animate_look(void) 01176 { 01177 gboolean valid; 01178 GtkTreeIter iter; 01179 item *tmp; 01180 static int inv_tick=0; 01181 01182 /* 01183 * If global tick is set, then we are getting tick events from server to 01184 * keep in sync, so we don't need the logic below. 01185 */ 01186 if (!tick) { 01187 /* 01188 * The gtk client timeout is 12 times faster than that of the server so 01189 * we slow it down here. If we were really clever, we'd find what the 01190 * timeout on the server actually is, and do gettimeofday calls here to 01191 * remain very closely in sync. 01192 */ 01193 inv_tick++; 01194 if (inv_tick < 12) return; 01195 inv_tick=0; 01196 } 01197 01198 /* Get the first iter in the list */ 01199 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store_look), &iter); 01200 01201 while (valid) { 01202 gtk_tree_model_get (GTK_TREE_MODEL(store_look), &iter, 01203 LIST_OBJECT, &tmp, 01204 -1); 01205 01206 /* This is an object with animations */ 01207 if (tmp->animation_id >0 && tmp->anim_speed) { 01208 tmp->last_anim++; 01209 01210 /* Time to change the face for this one */ 01211 if (tmp->last_anim >= tmp->anim_speed) { 01212 tmp->anim_state++; 01213 if (tmp->anim_state >= animations[tmp->animation_id].num_animations) 01214 tmp->anim_state=0; 01215 tmp->face = animations[tmp->animation_id].faces[tmp->anim_state]; 01216 tmp->last_anim=0; 01217 01218 /* Update image in the tree store */ 01219 gtk_tree_store_set(store_look, &iter, 01220 LIST_ICON, (GdkPixbuf*)pixmaps[tmp->face]->icon_image, 01221 -1); 01222 } 01223 } 01224 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(store_look), &iter); 01225 } 01226 } 01227 01232 void inventory_tick(void) 01233 { 01234 animate_inventory(); 01235 animate_look(); 01236 }
1.7.6.1