Crossfire Client, Branches  R11627
inventory.c
Go to the documentation of this file.
1 const char * const rcsid_gtk2_inventory_c =
2  "$Id: inventory.c 9201 2008-06-01 17:32:45Z anmaster $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2005-2007 Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire@metalforge.org
23 */
24 
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33 
34 #include <gtk/gtk.h>
35 #include <glade/glade.h>
36 
37 #include "client.h"
38 
39 #include "main.h"
40 #include "image.h"
41 #include "gtk2proto.h"
42 
43 #include "../../pixmaps/all.xpm"
44 #include "../../pixmaps/coin.xpm"
45 #include "../../pixmaps/hand.xpm"
46 #include "../../pixmaps/hand2.xpm"
47 #include "../../pixmaps/lock.xpm"
48 #include "../../pixmaps/mag.xpm"
49 #include "../../pixmaps/nonmag.xpm"
50 #include "../../pixmaps/skull.xpm"
51 #include "../../pixmaps/unlock.xpm"
52 
54 GtkTreeStore *store_look;
55 static double weight_limit;
56 static GtkTooltips *inv_table_tooltips;
57 
58 /*
59  * Hopefully, large enough. Trying to do this with malloc gets more
60  * complicated because position of elements within the array are important, so
61  * a simple realloc won't work.
62  */
63 #define MAX_INV_COLUMNS 20
64 #define MAX_INV_ROWS 100
66 
67 /* Different styles we recognize */
68 enum Styles {
70 };
71 
72 /* The name of these styles in the rc file */
73 static const char *Style_Names[Style_Last] = {
74  "inv_magical", "inv_cursed", "inv_unpaid", "inv_locked", "inv_applied"
75 };
76 
77 /* Actual styles as loaded. May be null if no style found. */
78 static GtkStyle *inv_styles[Style_Last];
79 
80 /*
81  * The basic idea of the NoteBook_Info structure is to hold everything we need
82  * to know about the different inventory notebooks in a module fashion -
83  * instead of hardcoding values, they can be held in the array.
84  */
85 #define NUM_INV_LISTS 10
86 #define INV_SHOW_ITEM 0x1
87 #define INV_SHOW_COLOR 0x2
88 
89 enum {
92 };
93 
95 
96 typedef struct {
97  const char *name;
98  const char *tooltip;
99  const char *const *xpm;
100  int(*show_func) (item *it);
104  int type;
109  GtkWidget *treeview;
110  GtkTreeStore *treestore;
111 } Notebook_Info;
112 
113 static int show_all(item *it) { return INV_SHOW_ITEM | INV_SHOW_COLOR; }
114 static int show_applied(item *it) { return (it->applied?INV_SHOW_ITEM:0); }
115 static int show_unapplied(item *it) { return (it->applied?0:INV_SHOW_ITEM); }
116 static int show_unpaid(item *it) { return (it->unpaid?INV_SHOW_ITEM:0); }
117 static int show_cursed(item *it) { return ((it->cursed | it->damned)?INV_SHOW_ITEM:0); }
118 static int show_magical(item *it) { return (it->magical?INV_SHOW_ITEM:0); }
119 static int show_nonmagical(item *it){ return (it->magical?0:INV_SHOW_ITEM); }
120 static int show_locked(item *it) { return (it->locked?(INV_SHOW_ITEM|INV_SHOW_COLOR):0); }
121 static int show_unlocked(item *it) { return (it->locked?0:(INV_SHOW_ITEM|INV_SHOW_COLOR)); }
122 
124 {"all", "All Items", all_xpm, show_all, INV_TREE},
125 {"applied", "Applied Items", hand_xpm, show_applied, INV_TREE},
126 {"unapplied", "Unapplied Items", hand2_xpm, show_unapplied, INV_TREE},
127 {"unpaid", "Unpaid items", coin_xpm, show_unpaid, INV_TREE},
128 {"cursed", "Cursed items", skull_xpm, show_cursed, INV_TREE},
129 {"magical", "Magical items", mag_xpm, show_magical, INV_TREE},
130 {"nonmagical", "Nonmagical items", nonmag_xpm, show_nonmagical, INV_TREE},
131 {"locked", "Inventory locked items", lock_xpm, show_locked, INV_TREE},
132 {"unlocked", "Inventory unlocked items",unlock_xpm, show_unlocked, INV_TREE},
133 {"icons", "Quick icon view", NULL, show_all, INV_TABLE}
134 };
135 
136 enum {
139 };
140 
141 #define ITEM_INVENTORY 0x1
142 #define ITEM_GROUND 0x2
143 #define ITEM_IN_CONTAINER 0x4
144 
155 static int get_item_env(item *it)
156 {
157  if (it->env == cpl.ob) return ITEM_INVENTORY;
158  if (it->env == cpl.below) return ITEM_GROUND;
159  if (it->env == NULL) return 0;
160  return (ITEM_IN_CONTAINER | get_item_env(it->env));
161 }
162 
168 static void list_item_action(GdkEventButton *event, item *tmp)
169 {
170  int env;
171 
172  /* We need to know where this item is in fact is */
173  env = get_item_env(tmp);
174 
175  /* It'd sure be nice if these weren't hardcoded values for button and
176  * shift presses.
177  */
178  if (event->button == 1) {
179  if (event->state & GDK_SHIFT_MASK)
180  toggle_locked(tmp);
181  else
182  client_send_examine (tmp->tag);
183  }
184  else if (event->button == 2) {
185  if (event->state & GDK_SHIFT_MASK)
186  send_mark_obj(tmp);
187  else
188  client_send_apply (tmp->tag);
189  }
190  else if (event->button == 3) {
191  if (tmp->locked) {
192  draw_info ("This item is locked. To drop it, first unlock by shift+leftclicking on it.",
193  NDI_BLACK);
194  } else {
195  uint32 dest;
196 
197  cpl.count = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spinbutton_count));
198  /*
199  * Figure out where to move the item to. If it is on the ground,
200  * it is moving to the players inventory. If it is in a container,
201  * it is also moving to players inventory. If it is in the players
202  * inventory (not a container) and the player has an open container
203  * in his inventory, move the object to the container (not ground).
204  * Otherwise, it is moving to the ground (dest=0). Have to look at
205  * the item environment, because what list is no longer accurate.
206  */
207  if (env & (ITEM_GROUND | ITEM_IN_CONTAINER))
208  dest = cpl.ob->tag;
209  else if (env == ITEM_INVENTORY && cpl.container &&
212  dest = cpl.container->tag;
213  } else
214  dest = 0;
215 
216  client_send_move (dest, tmp->tag, cpl.count);
217  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton_count),0.0);
218  cpl.count=0;
219  }
220  }
221 }
222 
238  GtkTreeSelection *selection,
239  GtkTreeModel *model,
240  GtkTreePath *path,
241  gboolean path_currently_selected,
242  gpointer userdata)
243 {
244  GtkTreeIter iter;
245  GdkEventButton *event;
246 
247  /* Get the current event so we can know if shift is pressed */
248  event = (GdkEventButton*)gtk_get_current_event();
249  if (!event) {
250  LOG(LOG_ERROR,"inventory.c:list_selection_func", "Unable to get event structure\n");
251  return FALSE;
252  }
253 
254  if (gtk_tree_model_get_iter(model, &iter, path)) {
255  item *tmp;
256 
257  gtk_tree_model_get(model, &iter, LIST_OBJECT, &tmp, -1);
258 
259  if (!tmp) {
260  LOG(LOG_ERROR,"inventory.c:list_selection_func", "Unable to get item structure\n");
261  return FALSE;
262  }
263  list_item_action(event, tmp);
264  }
265  /*
266  * Don't want the row toggled - our code above handles what we need to do,
267  * so return false.
268  */
269  return FALSE;
270 }
271 
281 void
282 list_row_collapse (GtkTreeView *treeview,
283  GtkTreeIter *iter,
284  GtkTreePath *path,
285  gpointer user_data)
286 {
287  GtkTreeModel *model;
288  item *tmp;
289 
290  model = gtk_tree_view_get_model(treeview);
291 
292  gtk_tree_model_get(GTK_TREE_MODEL(model), iter, LIST_OBJECT, &tmp, -1);
293  client_send_apply (tmp->tag);
294 }
295 
300 static void setup_list_columns(GtkWidget *treeview)
301 {
302  GtkCellRenderer *renderer;
303  GtkTreeViewColumn *column;
304  GtkTreeSelection *selection;
305 
306 #if 0
307  /*
308  * This is a hack to hide the expander column. We do this because access
309  * via containers need to be handled by the apply/unapply mechanism -
310  * otherwise, I think it will be confusing - people 'closing' the container
311  * with the expander arrow and still having things go into/out of the
312  * container. Unfortunat
313  */
314  renderer = gtk_cell_renderer_text_new ();
315  column = gtk_tree_view_column_new_with_attributes ("", renderer,
316  "text", LIST_NONE,
317  NULL);
318  gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
319  gtk_tree_view_column_set_visible(column, FALSE);
320  gtk_tree_view_set_expander_column(GTK_TREE_VIEW (treeview), column);
321 #endif
322 
323  renderer = gtk_cell_renderer_pixbuf_new ();
324  /*
325  * Setting the xalign to 0.0 IMO makes the display better. Gtk
326  * automatically resizes the column to make space based on image size,
327  * however, it isn't really aggressive on shrinking it. IMO, it looks
328  * better for the image to always be at the far left - without this
329  * alignment, the image is centered which IMO doesn't always look good.
330  */
331  g_object_set (G_OBJECT (renderer), "xalign", 0.0,
332  NULL);
333  column = gtk_tree_view_column_new_with_attributes ("?", renderer,
334  "pixbuf", LIST_ICON,
335  NULL);
336 
337 /* gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);*/
338  gtk_tree_view_column_set_min_width(column, image_size);
339  gtk_tree_view_column_set_sort_column_id(column, LIST_TYPE);
340  gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
341 
342  renderer = gtk_cell_renderer_text_new ();
343  column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
344  "text", LIST_NAME,
345  NULL);
346  gtk_tree_view_column_set_expand(column, TRUE);
347  gtk_tree_view_column_set_sort_column_id(column, LIST_BASENAME);
348 
349  gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
350  gtk_tree_view_column_add_attribute(column, renderer, "background-gdk", LIST_BACKGROUND);
351  gtk_tree_view_column_add_attribute(column, renderer, "foreground-gdk", LIST_FOREGROUND);
352  gtk_tree_view_column_add_attribute(column, renderer, "font-desc", LIST_FONT);
353  gtk_tree_view_set_expander_column(GTK_TREE_VIEW (treeview), column);
354  gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
355 
356  renderer = gtk_cell_renderer_text_new ();
357  column = gtk_tree_view_column_new_with_attributes ("Weight", renderer,
358  "text", LIST_WEIGHT,
359  NULL);
360  /*
361  * At 50, the title was always truncated on some systems. 64 is the
362  * minimum on those systems for it to be possible to avoid truncation at
363  * all. Truncating the title looks cheesy, especially since heavy items
364  * (100+) need the width of the field anyway. If weight pushed off the
365  * edge is a problem, it would be just better to let the user resize or
366  * find a way to allow rendering with a smaller font.
367  */
368  gtk_tree_view_column_set_min_width(column, 64);
369  gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
370 
371  gtk_tree_view_column_set_sort_column_id(column, LIST_WEIGHT);
372  gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
373  gtk_tree_view_column_add_attribute(column, renderer, "background-gdk", LIST_BACKGROUND);
374  gtk_tree_view_column_add_attribute(column, renderer, "foreground-gdk", LIST_FOREGROUND);
375  gtk_tree_view_column_add_attribute(column, renderer, "font-desc", LIST_FONT);
376  /*
377  * Really, we never really do selections - clicking on an object causes a
378  * reaction right then. So grab press before the selection and just negate
379  * the selection - that's more efficient than unselection the item after it
380  * was selected.
381  */
382  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
383 
384  gtk_tree_selection_set_select_function(selection, list_selection_func, NULL, NULL);
385 }
386 
395 {
396  int i;
397  GtkStyle *tmp_style;
398  static int has_init=0;
399 
400  for (i=0; i < Style_Last; i++) {
401  if (has_init && inv_styles[i]) g_object_unref(inv_styles[i]);
402  tmp_style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), NULL, Style_Names[i],
403  G_TYPE_NONE);
404  if (tmp_style) {
405  inv_styles[i] = g_object_ref(tmp_style);
406  }
407  else {
408  LOG(LOG_INFO, "inventory.c::inventory_get_styles", "Unable to find style for %s",
409  Style_Names[i]);
410  inv_styles[i] = NULL;
411  }
412  }
413  has_init=1;
414 }
415 
421 void inventory_init(GtkWidget *window_root)
422 {
423  int i;
424  GladeXML *xml_tree;
425 
427 
428  xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root));
429 
430  inv_notebook = glade_xml_get_widget(xml_tree,"notebook_inv");
431  treeview_look = glade_xml_get_widget(xml_tree, "treeview_look");
432  weight_label = glade_xml_get_widget(xml_tree,"label_inv_weight");
433  inv_table = glade_xml_get_widget(xml_tree,"inv_table");
434 
435  g_signal_connect((gpointer) inv_notebook, "switch_page",
436  (GCallback) on_notebook_switch_page, NULL);
437  g_signal_connect((gpointer) inv_table, "expose_event",
438  (GCallback) on_inv_table_expose_event, NULL);
439  g_signal_connect((gpointer) treeview_look, "row_collapsed",
440  (GCallback) list_row_collapse, NULL);
441 
442  inv_table_tooltips = gtk_tooltips_new();
443  gtk_tooltips_enable(inv_table_tooltips);
444 
445  memset(inv_table_children, 0, sizeof(GtkWidget *) * MAX_INV_ROWS * MAX_INV_COLUMNS);
446 
447  store_look = gtk_tree_store_new (LIST_NUM_COLUMNS,
448  G_TYPE_STRING,
449  G_TYPE_OBJECT,
450  G_TYPE_STRING,
451  G_TYPE_STRING,
452  G_TYPE_POINTER,
453  GDK_TYPE_COLOR,
454  G_TYPE_INT,
455  G_TYPE_STRING,
456  GDK_TYPE_COLOR,
457  PANGO_TYPE_FONT_DESCRIPTION);
458 
459  gtk_tree_view_set_model(GTK_TREE_VIEW(treeview_look), GTK_TREE_MODEL(store_look));
460  setup_list_columns(treeview_look);
461  /*
462  * Glade doesn't let us fully realize a treeview widget - we still need to
463  * to do a bunch of customization just like we do for the look window
464  * above. If we have to do all that work, might as well just put it in the
465  * for loop below vs setting up half realized widgets within glade that we
466  * then need to finish setting up. However, that said, we want to be able
467  * to set up other notebooks within glade for perhaps a true list of just
468  * icons. So we presume that any tabs that exist must already be all set
469  * up. We prepend our tabs to the existing tab - this makes the position
470  * of the array of noteboks correspond to actual data in the tabs.
471  */
472  for (i=0; i < NUM_INV_LISTS; i++) {
473  GtkWidget *swindow, *image;
474 
475  if (inv_notebooks[i].type == INV_TREE) {
476  swindow = gtk_scrolled_window_new(NULL, NULL);
477  gtk_widget_show(swindow);
478  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow),
479  GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
480  image = gtk_image_new_from_pixbuf(
481  gdk_pixbuf_new_from_xpm_data((const char**)inv_notebooks[i].xpm));
482 
483  if (inv_notebooks[i].tooltip) {
484  GtkWidget *eb;
485 
486  eb=gtk_event_box_new();
487  gtk_widget_show(eb);
488 
489  gtk_container_add(GTK_CONTAINER(eb), image);
490  gtk_widget_show(image);
491 
492  image=eb;
493  gtk_tooltips_set_tip(inv_table_tooltips, image, inv_notebooks[i].tooltip, NULL);
494  }
495 
496  gtk_notebook_insert_page(GTK_NOTEBOOK(inv_notebook), swindow, image, i);
497 
498  inv_notebooks[i].treestore = gtk_tree_store_new (LIST_NUM_COLUMNS,
499  G_TYPE_STRING,
500  G_TYPE_OBJECT,
501  G_TYPE_STRING,
502  G_TYPE_STRING,
503  G_TYPE_POINTER,
504  GDK_TYPE_COLOR,
505  G_TYPE_INT,
506  G_TYPE_STRING,
507  GDK_TYPE_COLOR,
508  PANGO_TYPE_FONT_DESCRIPTION);
509 
510  inv_notebooks[i].treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
511  inv_notebooks[i].treestore));
512 
513  g_signal_connect ((gpointer) inv_notebooks[i].treeview, "row_collapsed",
514  G_CALLBACK (list_row_collapse), NULL);
515 
516  setup_list_columns(inv_notebooks[i].treeview);
517  gtk_widget_show(inv_notebooks[i].treeview);
518  gtk_container_add(GTK_CONTAINER(swindow), inv_notebooks[i].treeview);
519  }
520  }
521  num_inv_notebook_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(inv_notebook));
522 
523  /* Make sure we are on the first page */
524  gtk_notebook_set_current_page(GTK_NOTEBOOK(inv_notebook), 0);
525 
526  /* If all the data is set up properly, these should match */
527  if (num_inv_notebook_pages != NUM_INV_LISTS) {
528  LOG(LOG_ERROR,"inventory.c:inventory_init",
529  "num_inv_notebook_pages (%d) does not match NUM_INV_LISTS(%d)\n",
530  num_inv_notebook_pages, NUM_INV_LISTS);
531  }
532 
533 }
534 
539 void set_show_icon (const char *s)
540 {
541 }
542 
547 void set_show_weight (const char *s)
548 {
549 }
550 
560 {
561  draw_lists();
562 }
563 
569 {
570  draw_lists();
571 }
572 
577 void command_show (const char *params)
578 {
579  if(!params) {
580  /*
581  * Shouldn't need to get current page, but next_page call is not
582  * wrapping like the docs claim it should.
583  */
584  if (gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook))==num_inv_notebook_pages)
585  gtk_notebook_set_page(GTK_NOTEBOOK(inv_notebook), 0);
586  else
587  gtk_notebook_next_page(GTK_NOTEBOOK(inv_notebook));
588  } else {
589  int i;
590  char buf[MAX_BUF];
591 
592  for (i=0; i < NUM_INV_LISTS; i++) {
593  if (!strncmp(params, inv_notebooks[i].name, strlen(params))) {
594  gtk_notebook_set_page(GTK_NOTEBOOK(inv_notebook), i);
595  return;
596  }
597  }
598  snprintf(buf, sizeof(buf), "Unknown notebook page %s\n", params);
599  draw_info(buf, NDI_RED);
600  }
601 }
602 
610 {
611  weight_limit = wlim/ 1000.0;
612 }
613 
619 static GtkStyle *get_row_style(item *it)
620 {
621  int style;
622 
623  /* Note that this ordering is documented in the sample rc file.
624  * it would be nice if this precedence could be more easily
625  * setable by the end user.
626  */
627  if (it->unpaid) style=Style_Unpaid;
628  else if (it->cursed || it->damned) style=Style_Cursed;
629  else if (it->magical) style = Style_Magical;
630  else if (it->applied) style = Style_Applied;
631  else if (it->locked) style = Style_Locked;
632  else return NULL; /* No matching style */
633 
634  return inv_styles[style];
635 }
636 
637 /***************************************************************************
638  * Below are the actual guts for drawing the inventory and look windows.
639  * Some quick notes:
640  * 1) The gtk2 widgets (treeview/treemodel) seem noticably slower than the
641  * older clist widgets in gtk1. This is beyond the code below - just
642  * scrolling the window, which is all done internally by gtk, is
643  * quite slow. Seems especially bad when using the scrollwheel.
644  * 2) documentation suggests the detaching the treemodel and re-attaching
645  * it after insertions would be faster. The problem is that this causes
646  * loss of positioning for the scrollbar. Eg, you eat a food in the middle
647  * of your inventory, and then inventory resets to the top of the inventory.
648  * 3) it'd probably be much more efficient if the code could know what changes
649  * are taking place, instead of rebuilding the tree model each time. For
650  * example, if the only thing that changed is the number of of the object,
651  * we can just update the name and weight, and not rebuild the entire list.
652  * This may be doable in the code below by getting data from the tree store
653  * and comparing it against what we want to show - however, figuring out
654  * insertions and removals are more difficult.
655  */
656 
660 
674 static void add_object_to_store(item *it, GtkTreeStore *store,
675  GtkTreeIter *new, GtkTreeIter *parent, int color)
676 {
677  char buf[256], buf1[256];
678  GdkColor *foreground=NULL, *background=NULL;
679  PangoFontDescription *font=NULL;
680  GtkStyle *row_style;
681 
682  if(it->weight < 0) {
683  strcpy (buf," ");
684  } else {
685  snprintf(buf, sizeof(buf), "%6.1f" ,it->nrof * it->weight);
686  }
687  snprintf(buf1, 255, "%s %s", it->d_name, it->flags);
688  if (color) {
689  row_style = get_row_style(it);
690  if (row_style) {
691  /*
692  * Even if the user doesn't define these, we should still get get
693  * defaults from the system.
694  */
695  foreground = &row_style->text[GTK_STATE_NORMAL];
696  background = &row_style->base[GTK_STATE_NORMAL];
697  font = row_style->font_desc;
698  }
699  }
700 
701  gtk_tree_store_append (store, new, parent); /* Acquire an iterator */
702  gtk_tree_store_set (store, new,
703  LIST_ICON, (GdkPixbuf*)pixmaps[it->face]->icon_image,
704  LIST_NAME, buf1,
705  LIST_WEIGHT, buf,
706  LIST_BACKGROUND, background,
707  LIST_FOREGROUND, foreground,
708  LIST_FONT, font,
709  LIST_OBJECT, it,
710  LIST_TYPE, it->type,
711  LIST_BASENAME, it->s_name,
712  -1);
713 }
714 
718 void draw_look_list(void)
719 {
720  item *tmp;
721  GtkTreeIter iter;
722  /*
723  * List drawing is actually fairly inefficient - we only know globally if
724  * the objects has changed, but have no idea what specific object has
725  * changed. As such, we are forced to basicly redraw the entire list each
726  * time this is called.
727  */
728  gtk_tree_store_clear(store_look);
729 
730  for (tmp=cpl.below->inv; tmp; tmp=tmp->next) {
731  add_object_to_store(tmp, store_look, &iter, NULL, 1);
732 
733  if ((cpl.container == tmp) && tmp->open) {
734  item *tmp2;
735  GtkTreeIter iter1;
736  GtkTreePath *path;
737 
738  for (tmp2 = tmp->inv; tmp2; tmp2=tmp2->next) {
739  add_object_to_store(tmp2, store_look, &iter1, &iter, 1);
740  }
741  path = gtk_tree_model_get_path(GTK_TREE_MODEL(store_look), &iter);
742  gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview_look), path, FALSE);
743  gtk_tree_path_free (path);
744  }
745  }
746 }
747 
754 void draw_inv_list(int tab)
755 {
756  item *tmp;
757  GtkTreeIter iter;
758  int rowflag;
759 
760  /*
761  * List drawing is actually fairly inefficient - we only know globally if
762  * the objects has changed, but have no idea what specific object has
763  * changed. As such, we are forced to basicly redraw the entire list each
764  * time this is called.
765  */
766  gtk_tree_store_clear(inv_notebooks[tab].treestore);
767 
768  for (tmp=cpl.ob->inv; tmp; tmp=tmp->next) {
769  rowflag = inv_notebooks[tab].show_func(tmp);
770  if (!(rowflag & INV_SHOW_ITEM)) continue;
771 
772  add_object_to_store(tmp, inv_notebooks[tab].treestore, &iter, NULL, rowflag & INV_SHOW_COLOR);
773 
774  if ((cpl.container == tmp) && tmp->open) {
775  item *tmp2;
776  GtkTreeIter iter1;
777  GtkTreePath *path;
778 
779  for (tmp2 = tmp->inv; tmp2; tmp2=tmp2->next) {
780  /*
781  * Wonder if we really want this logic for objects in
782  * containers? my thought is yes - being able to see all
783  * cursed objects in the container could be quite useful.
784  * Unfortunately, that doesn't quite work as intended, because
785  * we will only get here if the container object is being
786  * displayed. Since container objects can't be cursed, can't
787  * use that as a filter.
788  */
789  /*
790  rowflag = inv_notebooks[tab].show_func(tmp2);
791  */
792  if (!(rowflag & INV_SHOW_ITEM)) continue;
793  add_object_to_store(tmp2, inv_notebooks[tab].treestore, &iter1, &iter,
794  rowflag & INV_SHOW_COLOR);
795  }
796  path = gtk_tree_model_get_path(GTK_TREE_MODEL(inv_notebooks[tab].treestore), &iter);
797  gtk_tree_view_expand_row(GTK_TREE_VIEW(inv_notebooks[tab].treeview), path, FALSE);
798  gtk_tree_path_free (path);
799  }
800  }
801 }
802 
810 gboolean
812  GdkEventButton *event,
813  gpointer user_data)
814 {
815  list_item_action(event, (item*)user_data);
816  return TRUE;
817 }
818 
826 gboolean
828  GdkEventExpose *event,
829  gpointer user_data)
830 {
831  item *tmp;
832 
833  tmp = (item*)user_data;
834 
835  gdk_window_clear(widget->window);
836  /*
837  * Can get cases when switching tabs that we get an expose event before the
838  * list is updated - if so, don't draw stuff we don't have faces for.
839  */
840  if (tmp->face)
841  gdk_draw_pixbuf(widget->window, NULL,
842  (GdkPixbuf*)pixmaps[tmp->face]->icon_image,
843  0, 0, 0, 0, image_size, image_size, GDK_RGB_DITHER_NONE, 0, 0);
844  return TRUE;
845 }
846 
847 #define INVHELPTEXT "Left click examines the object. Middle click applies \
848 the object. Right click drops the object. Shift left click locks/unlocks the \
849 object. Shift middle click marks the object"
850 
857 void draw_inv_table(int animate)
858 {
859  int x, y, rows, columns, num_items, i;
860  static int max_drawn=0;
861  item *tmp;
862  char buf[256];
863  gulong handler;
864 
865  num_items=0;
866  for (tmp=cpl.ob->inv; tmp; tmp=tmp->next)
867  num_items++;
868 
869  columns = inv_table->allocation.width / image_size;
870  if (columns > MAX_INV_COLUMNS) columns = MAX_INV_COLUMNS;
871  rows = inv_table->allocation.height / image_size;
872 
873  if (num_items > columns * rows) {
874  rows = num_items / columns;
875  if (num_items % columns) rows++;
876  }
877  if (rows > MAX_INV_ROWS) rows=MAX_INV_ROWS;
878 
879  gtk_table_resize(GTK_TABLE(inv_table), rows, columns);
880 
881  x=0;
882  y=0;
883  for (tmp=cpl.ob->inv; tmp; tmp=tmp->next) {
884  if (inv_table_children[x][y] == NULL) {
885  inv_table_children[x][y] = gtk_drawing_area_new();
886  gtk_drawing_area_size (GTK_DRAWING_AREA(inv_table_children[x][y]),
888 
889  gtk_table_attach(GTK_TABLE(inv_table), inv_table_children[x][y],
890  x, x+1, y, y+1, GTK_FILL, GTK_FILL, 0, 0);
891  }
892  if (animate) {
893  /* This is an object with animations */
894  if (tmp->animation_id >0 && tmp->anim_speed) {
895  tmp->last_anim++;
896 
897  /* Time to change the face for this one */
898  if (tmp->last_anim >= tmp->anim_speed) {
899  tmp->anim_state++;
901  tmp->anim_state=0;
902  tmp->face = animations[tmp->animation_id].faces[tmp->anim_state];
903  tmp->last_anim=0;
904 
905  gdk_window_clear(inv_table_children[x][y]->window);
906  gdk_draw_pixbuf(inv_table_children[x][y]->window, NULL,
907  (GdkPixbuf*)pixmaps[tmp->face]->icon_image,
908  0, 0, 0, 0, image_size, image_size, GDK_RGB_DITHER_NONE, 0, 0);
909  }
910  }
911  /* On animation run, so don't do any of the remaining logic */
912  } else {
913  /*
914  * Need to clear out the old signals, since the signals are
915  * effectively stacked - you can have 6 signal handlers tied to the
916  * same function.
917  */
918  handler = g_signal_handler_find((gpointer)inv_table_children[x][y],
919  G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
921  NULL);
922 
923  if (handler)
924  g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler);
925 
926  handler = g_signal_handler_find((gpointer)inv_table_children[x][y],
927  G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
929  NULL);
930  if (handler)
931  g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler);
932  /*
933  * Not positive precisely what events are needed, but some events
934  * beyond just the button press are necessary for the tooltips to
935  * work.
936  */
937  gtk_widget_add_events (inv_table_children[x][y], GDK_ALL_EVENTS_MASK);
938 
939  g_signal_connect ((gpointer) inv_table_children[x][y], "button_press_event",
941  tmp);
942 
943  g_signal_connect ((gpointer) inv_table_children[x][y], "expose_event",
945  tmp);
946 
947  gdk_window_clear(inv_table_children[x][y]->window);
948  gdk_draw_pixbuf(inv_table_children[x][y]->window, NULL,
949  (GdkPixbuf*)pixmaps[tmp->face]->icon_image,
950  0, 0, 0, 0, image_size, image_size, GDK_RGB_DITHER_NONE, 0, 0);
951 
952  gtk_widget_show(inv_table_children[x][y]);
953  /*
954  * Use tooltips to provide additional detail about the icons.
955  * Looking at the code, the tooltip widget will take care of
956  * removing the old tooltip, freeing strings, etc.
957  */
958  snprintf(buf, 255, "%s %s", tmp->d_name, tmp->flags);
959  gtk_tooltips_set_tip(inv_table_tooltips, inv_table_children[x][y],
960  buf, INVHELPTEXT);
961  }
962  x++;
963  if (x == columns) {
964  x=0;
965  y++;
966  }
967 
968  }
969  /* Don't need to do the logic below if only doing animation run */
970  if (animate) return;
971  /*
972  * Need to disconnect the callback functions cells we did not draw.
973  * otherwise, we get errors on objects that are drawn.
974  */
975  for (i=num_items; i<=max_drawn; i++) {
976  if (inv_table_children[x][y]) {
977  gdk_window_clear(inv_table_children[x][y]->window);
978 
979  handler = g_signal_handler_find((gpointer)inv_table_children[x][y],
980  G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
982  NULL);
983 
984  if (handler)
985  g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler);
986 
987  handler = g_signal_handler_find((gpointer)inv_table_children[x][y],
988  G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
990  NULL);
991  if (handler)
992  g_signal_handler_disconnect((gpointer) inv_table_children[x][y], handler);
993 
994  /* Hide the widget so that the tooltips doesn't show up */
995  gtk_widget_hide(inv_table_children[x][y]);
996  }
997  x++;
998  if (x == columns) {
999  x=0;
1000  y++;
1001  }
1002  }
1003  max_drawn = num_items;
1004 
1005  gtk_widget_show(inv_table);
1006 }
1007 
1013 void draw_inv(int tab)
1014 {
1015  char buf[256];
1016 
1017  snprintf(buf, sizeof(buf), "%6.1f/%6.1f", cpl.ob->weight, weight_limit);
1018  gtk_label_set(GTK_LABEL(weight_label), buf);
1019 
1020  if (inv_notebooks[tab].type == INV_TREE)
1021  draw_inv_list(tab);
1022  else if (inv_notebooks[tab].type == INV_TABLE)
1023  draw_inv_table(0);
1024 }
1025 
1029 void draw_lists (void)
1030 {
1031  cpl.below->inv_updated=1;
1032  /*
1033  * There are some extra complications with container handling and timing.
1034  * For example, we draw the window before we get a list of the container,
1035  * and then the container contents are not drawn - this can be handled by
1036  * looking at container->inv_updated.
1037  */
1039  cpl.container->env->inv_updated = 1;
1041  }
1042  if (cpl.ob->inv_updated) {
1043  draw_inv(gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook)));
1044  cpl.ob->inv_updated=0;
1045  }
1046  if (cpl.below->inv_updated) {
1047  draw_look_list();
1048  cpl.below->inv_updated=0;
1049  }
1050 }
1051 
1064 void
1065 on_notebook_switch_page (GtkNotebook *notebook,
1066  GtkNotebookPage *page,
1067  guint page_num,
1068  gpointer user_data)
1069 {
1070  int oldpage;
1071 
1072  oldpage = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
1073  if (oldpage != page_num && inv_notebooks[oldpage].type == INV_TREE)
1074  gtk_tree_store_clear(inv_notebooks[oldpage].treestore);
1075  cpl.ob->inv_updated=1;
1076 }
1077 
1085 gboolean
1086 on_inv_table_expose_event (GtkWidget *widget,
1087  GdkEventExpose *event,
1088  gpointer user_data)
1089 {
1090  draw_inv_table(0);
1091  return TRUE;
1092 }
1093 
1098 {
1099  gboolean valid;
1100  GtkTreeIter iter;
1101  item *tmp;
1102  int page;
1103  GtkTreeStore *store;
1104  static int inv_tick=0;
1105 
1106  /*
1107  * If global tick is set, then we are getting tick events from server to
1108  * keep in sync, so we don't need the logic below.
1109  */
1110  if (!tick) {
1111  /*
1112  * The gtk client timeout is 12 times faster than that of the server so
1113  * we slow it down here. If we were really clever, we'd find what the
1114  * timeout on the server actually is, and do gettimeofday calls here to
1115  * remain very closely in sync.
1116  */
1117  inv_tick++;
1118  if (inv_tick < 12) return;
1119  inv_tick=0;
1120  }
1121 
1122  page = gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook));
1123 
1124  /* Still need to do logic for the table view. */
1125  if (inv_notebooks[page].type == INV_TABLE) {
1126  draw_inv_table(1);
1127  return;
1128  }
1129 
1130  store = inv_notebooks[page].treestore;
1131 
1132  /* Get the first iter in the list */
1133  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter);
1134 
1135  while (valid) {
1136  gtk_tree_model_get (GTK_TREE_MODEL(store), &iter,
1137  LIST_OBJECT, &tmp,
1138  -1);
1139 
1140  /* This is an object with animations */
1141  if (tmp->animation_id >0 && tmp->anim_speed) {
1142  tmp->last_anim++;
1143 
1144  /* Time to change the face for this one */
1145  if (tmp->last_anim >= tmp->anim_speed) {
1146  tmp->anim_state++;
1148  tmp->anim_state=0;
1149  tmp->face = animations[tmp->animation_id].faces[tmp->anim_state];
1150  tmp->last_anim=0;
1151 
1152  /* Update image in the tree store */
1153  gtk_tree_store_set(store, &iter,
1154  LIST_ICON, (GdkPixbuf*)pixmaps[tmp->face]->icon_image,
1155  -1);
1156  }
1157  }
1158  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(store), &iter);
1159  }
1160 }
1161 
1165 void animate_look(void)
1166 {
1167  gboolean valid;
1168  GtkTreeIter iter;
1169  item *tmp;
1170  static int inv_tick=0;
1171 
1172  /*
1173  * If global tick is set, then we are getting tick events from server to
1174  * keep in sync, so we don't need the logic below.
1175  */
1176  if (!tick) {
1177  /*
1178  * The gtk client timeout is 12 times faster than that of the server so
1179  * we slow it down here. If we were really clever, we'd find what the
1180  * timeout on the server actually is, and do gettimeofday calls here to
1181  * remain very closely in sync.
1182  */
1183  inv_tick++;
1184  if (inv_tick < 12) return;
1185  inv_tick=0;
1186  }
1187 
1188  /* Get the first iter in the list */
1189  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store_look), &iter);
1190 
1191  while (valid) {
1192  gtk_tree_model_get (GTK_TREE_MODEL(store_look), &iter,
1193  LIST_OBJECT, &tmp,
1194  -1);
1195 
1196  /* This is an object with animations */
1197  if (tmp->animation_id >0 && tmp->anim_speed) {
1198  tmp->last_anim++;
1199 
1200  /* Time to change the face for this one */
1201  if (tmp->last_anim >= tmp->anim_speed) {
1202  tmp->anim_state++;
1204  tmp->anim_state=0;
1205  tmp->face = animations[tmp->animation_id].faces[tmp->anim_state];
1206  tmp->last_anim=0;
1207 
1208  /* Update image in the tree store */
1209  gtk_tree_store_set(store_look, &iter,
1210  LIST_ICON, (GdkPixbuf*)pixmaps[tmp->face]->icon_image,
1211  -1);
1212  }
1213  }
1214  valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(store_look), &iter);
1215  }
1216 }
1217 
1222 void inventory_tick(void)
1223 {
1225  animate_look();
1226 }
float weight
Definition: item.h:56
Animations animations[MAXANIM]
Definition: commands.c:419
GtkTreeStore * treestore
Definition: inventory.c:110
char s_name[NAME_LEN]
Definition: item.h:51
static XFontStruct * font
Definition: x11.c:192
uint32 count
Definition: client.h:295
void list_row_collapse(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data)
Definition: inventory.c:282
void item_event_item_changed(item *op)
Definition: inventory.c:489
static int show_unapplied(item *it)
Definition: inventory.c:115
void set_show_icon(const char *s)
Definition: inventory.c:1303
static int num_inv_notebook_pages
Definition: inventory.c:94
static const char * Style_Names[Style_Last]
Definition: inventory.c:73
GtkWidget * treeview_look
Definition: inventory.c:53
void command_show(const char *params)
Definition: inventory.c:1153
static int show_magical(item *it)
Definition: inventory.c:118
static XEvent event
Definition: x11.c:193
GtkWidget * spinbutton_count
Definition: keys.c:76
char flags[NAME_LEN]
Definition: item.h:53
uint8 anim_speed
Definition: item.h:59
item * ob
Definition: client.h:272
#define ITEM_GROUND
Definition: inventory.c:142
gboolean list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer userdata)
Definition: inventory.c:237
struct item_struct * env
Definition: item.h:48
void draw_look_list(void)
Definition: inventory.c:718
snd_pcm_hw_params_t * params
Definition: alsa9.c:111
void inventory_tick(void)
Definition: inventory.c:1374
const char * tooltip
Definition: inventory.c:98
struct item_struct * next
Definition: item.h:46
static unsigned long background
Definition: x11.c:187
static int show_applied(item *it)
Definition: inventory.c:114
GtkWidget * inv_table_children[MAX_INV_ROWS][MAX_INV_COLUMNS]
Definition: inventory.c:65
void draw_inv_table(int animate)
Definition: inventory.c:857
void item_event_item_deleting(item *op)
Definition: inventory.c:596
void animate_inventory(void)
Definition: inventory.c:1097
static GtkStyle * get_row_style(item *it)
Definition: inventory.c:619
static GtkStyle * inv_styles[Style_Last]
Definition: inventory.c:78
int(* show_func)(item *it)
Definition: inventory.c:100
uint16 inv_updated
Definition: item.h:70
static int show_all(item *it)
Definition: inventory.c:113
#define ITEM_INVENTORY
Definition: inventory.c:141
void inventory_get_styles(void)
Definition: inventory.c:394
static void list_item_action(GdkEventButton *event, item *tmp)
Definition: inventory.c:168
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: gx11.c:118
Notebook_Info inv_notebooks[NUM_INV_LISTS]
Definition: inventory.c:123
#define NUM_INV_LISTS
Definition: inventory.c:85
gboolean on_inv_table_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
Definition: inventory.c:1086
uint16 applied
Definition: item.h:67
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
void client_send_apply(int tag)
Definition: player.c:80
#define NDI_RED
Definition: newclient.h:204
void set_show_weight(const char *s)
Definition: inventory.c:1258
GtkTreeStore * store_look
Definition: inventory.c:54
#define TRUE
Definition: client-types.h:71
uint32 tick
Definition: client.c:70
GtkWidget * window_root
Definition: main.c:56
void set_weight_limit(uint32 wlim)
Definition: inventory.c:1245
void close_container(item *op)
Definition: inventory.c:1342
uint16 locked
Definition: item.h:66
static int show_unpaid(item *it)
Definition: inventory.c:116
char d_name[NAME_LEN]
Definition: item.h:50
static unsigned long foreground
Definition: x11.c:187
void * icon_image
Definition: gx11.h:61
uint16 animation_id
Definition: item.h:58
item * below
Definition: client.h:273
gboolean drawingarea_inventory_table_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition: inventory.c:811
Client_Player cpl
Definition: client.c:77
#define INV_SHOW_COLOR
Definition: inventory.c:87
item * container
Definition: client.h:275
void item_event_container_clearing(item *op)
Definition: inventory.c:525
sint32 tag
Definition: item.h:54
static int show_nonmagical(item *it)
Definition: inventory.c:119
void client_send_move(int loc, int tag, int nrof)
Definition: player.c:92
char * name
Definition: image.c:61
GtkWidget * weight_label
Definition: inventory.c:53
uint16 magical
Definition: item.h:62
static GtkTooltips * inv_table_tooltips
Definition: inventory.c:56
void animate_look(void)
Definition: inventory.c:1165
static void add_object_to_store(item *it, GtkTreeStore *store, GtkTreeIter *new, GtkTreeIter *parent, int color)
Definition: inventory.c:674
static void setup_list_columns(GtkWidget *treeview)
Definition: inventory.c:300
Styles
Definition: inventory.c:68
void send_mark_obj(item *op)
Definition: item.c:590
#define MAX_INV_COLUMNS
Definition: inventory.c:63
void toggle_locked(item *op)
Definition: item.c:573
const char * name
Definition: inventory.c:97
#define MAX_BUF
Definition: client-types.h:128
uint16 type
Definition: item.h:75
uint16 unpaid
Definition: item.h:65
uint8 anim_state
Definition: item.h:60
unsigned int uint32
Definition: client-types.h:77
gboolean drawingarea_inventory_table_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
Definition: inventory.c:827
uint16 open
Definition: item.h:68
#define INV_SHOW_ITEM
Definition: inventory.c:86
void draw_inv(int tab)
Definition: inventory.c:1013
static int show_unlocked(item *it)
Definition: inventory.c:121
void draw_inv_list(int tab)
Definition: inventory.c:754
static int has_init
Definition: spells.c:63
uint32 nrof
Definition: item.h:55
GtkWidget * treeview
Definition: inventory.c:109
void inventory_init(GtkWidget *window_root)
Definition: inventory.c:421
GtkWidget * inv_notebook
Definition: inventory.c:53
void client_send_examine(int tag)
Definition: player.c:85
void draw_info(const char *str, int color)
Definition: gx11.c:1773
static double weight_limit
Definition: inventory.c:55
uint16 last_anim
Definition: item.h:61
#define ITEM_IN_CONTAINER
Definition: inventory.c:143
GtkWidget * inv_table
Definition: inventory.c:53
void on_notebook_switch_page(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data)
Definition: inventory.c:1065
static int show_cursed(item *it)
Definition: inventory.c:117
void open_container(item *op)
Definition: inventory.c:1334
uint16 damned
Definition: item.h:64
#define NDI_BLACK
Definition: newclient.h:201
struct item_struct * inv
Definition: item.h:49
#define FALSE
Definition: client-types.h:68
uint8 num_animations
Definition: client.h:73
const char *const rcsid_gtk2_inventory_c
Definition: inventory.c:1
int image_size
Definition: gx11.c:116
#define MAX_INV_ROWS
Definition: inventory.c:64
void draw_lists(void)
Definition: inventory.c:1029
static int get_item_env(item *it)
Definition: inventory.c:155
uint16 cursed
Definition: item.h:63
static int show_locked(item *it)
Definition: inventory.c:120
sint16 face
Definition: item.h:57
const char *const * xpm
Definition: inventory.c:99
uint16 * faces
Definition: client.h:79
#define INVHELPTEXT
Definition: inventory.c:847