Crossfire Client, Branch  R11627
inventory.c
Go to the documentation of this file.
00001 
00002 /*
00003  * Showing and manipulating your inventory and what's in a
00004  * container/at your feet. (But not autopickup.)
00005  */
00006 
00007 #ifdef WIN32
00008 #include <config.h>
00009 #endif
00010 #include "gx11.h"
00011 #include "client.h" /* LOG */
00012 
00013 #define bool uint8 /* Grr! Pick *something*, please! */
00014 
00015 extern GdkColor gdk_grey;
00016 extern GdkColor gdk_black;
00017 extern GtkWidget *spellinventory; /* Defined in gx11.c */
00018 
00019 /* *grumble* Neither of two C textbooks I checked gave an example of this. :S */
00020 typedef bool (*itemfilter)(item * it);
00021 
00022 typedef struct {
00023     item * cont; /* The container whose contents we're showing. */
00024     item * move_dest; /* The container you want to move things to when you right-click on them. */
00025     GtkWidget * list; /* GtkCList */
00026     GtkWidget * scroll_window; /* Scrolled-window widget holding list. */
00027     itemfilter shows;
00028     bool complete_rebuild:1; /* Dirty flag. */
00029     bool show_weight:1;
00030     bool highlight:1;
00031     bool show_flags:1;
00032 
00033     /*
00034      * Image-column (and row height...) resizes to hold biggest face;
00035      * good when standing on buildings (or monsters!).
00036      */
00037     bool face_column_resizes:1;
00038 
00039     sint16 image_width;
00040     sint16 image_height;
00041 } inventory_viewer;
00042 
00043 /*
00044  * Creation
00045  */
00046 
00047 static GList * views;
00048 
00049 /* forward */
00050 static void list_button_event(
00051     GtkWidget *gtklist,
00052     gint row, gint column,
00053     GdkEventButton *event,
00054     inventory_viewer * view);
00055 
00056 
00057 static inventory_viewer * new_inventory_viewer(item * container, itemfilter filter, item * move_dest) {
00058     inventory_viewer * ret;
00059     GtkWidget * list;
00060     GtkWidget * scroll_window;
00061     GtkStyle * liststyle;
00062     gchar *titles[] = {"?", "Name", "Weight"};
00063 
00064     scroll_window = gtk_scrolled_window_new (0,0);
00065 
00066     list = gtk_clist_new_with_titles(3, titles);
00067 
00068     g_assert(list != NULL);
00069     gtk_clist_set_column_width (GTK_CLIST(list), 0, image_size);
00070     gtk_clist_set_column_width (GTK_CLIST(list), 1, 150);
00071     gtk_clist_set_column_width (GTK_CLIST(list), 2, 50);
00072 
00073     gtk_clist_set_selection_mode (GTK_CLIST(list) , GTK_SELECTION_SINGLE);
00074     gtk_clist_set_row_height (GTK_CLIST(list), image_size);
00075 
00076     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll_window),
00077                                     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
00078 
00079     liststyle = gtk_rc_get_style (list);
00080     if (liststyle) {
00081       liststyle->bg[GTK_STATE_SELECTED] = gdk_grey;
00082       liststyle->fg[GTK_STATE_SELECTED] = gdk_black;
00083       gtk_widget_set_style (list, liststyle);
00084     }
00085 
00086     gtk_widget_show(list);
00087 
00088     gtk_container_add(GTK_CONTAINER(scroll_window), list);
00089     gtk_widget_show(scroll_window);
00090 
00091     ret = malloc(sizeof(inventory_viewer));
00092     ret->complete_rebuild = TRUE;
00093     ret->list = list;
00094     ret->scroll_window = scroll_window;
00095     ret->cont = container;
00096     ret->shows = filter;
00097     ret->show_weight = TRUE;
00098     ret->show_flags = TRUE;
00099     ret->highlight = FALSE;
00100     ret->move_dest = move_dest;
00101     ret->face_column_resizes = FALSE;
00102     ret->image_height = ret->image_width = image_size;
00103     views = g_list_first(g_list_append(views, ret));
00104 
00105     gtk_clist_set_button_actions(GTK_CLIST(list),
00106         1, GTK_BUTTON_SELECTS);
00107     gtk_clist_set_button_actions(GTK_CLIST(list),
00108         2, GTK_BUTTON_SELECTS);
00109     gtk_signal_connect(GTK_OBJECT(list), "select_row",
00110         GTK_SIGNAL_FUNC(list_button_event),
00111         ret);
00112 
00113     return ret;
00114 }
00115 
00116 
00117 /* -------------------------------------------------------------------------------------- */
00118 
00119 /*
00120  * Old way only stored *animated* item to (widget, row); here, we'll record *every*
00121  * item. (And if someone wants to backport Wedel's icon-widget, make it (widget, type, location).)
00122  *
00123  * However, also record the animated items only for speed.
00124  *
00125  * TODO Test on a slower machine than the one I have right now; throw the profiler at it, figure
00126  * out if this is a bottleneck, and if it is which of n different ways of storing this information
00127  * is quickest.
00128  */
00129 
00130 typedef struct {
00131     item * it;
00132     GList * viewers;
00133 } item_delete;
00134 
00135 static GList * item_to_widgets = NULL;
00136 static GList * animated_items = NULL;
00137 
00138 /*
00139  * Store and retrieve
00140  */
00141 
00142 /* TODO Ideally, this goes away and we store the list in the item, (As a
00143 void pointer, of course.) However, we need to be able to check *all*
00144 known items; common doesn't really support that right now. */
00145 static item_delete * item_to_widget_retrieve(item * op) {
00146     GList * p;
00147     item_delete * ret = NULL;
00148 
00149     for (p = g_list_first(item_to_widgets); p != NULL; p = g_list_next(p) ) {
00150         item_delete * record;
00151 
00152         record = (item_delete *)(p->data);
00153 
00154         if (record->it == op) {
00155             return record;
00156         }
00157     }
00158 
00159     /* It's not on the list; we'll have to add one. */
00160     ret = malloc(sizeof(item_delete));
00161 
00162     ret->it = op;
00163     ret->viewers = NULL;
00164 
00165     item_to_widgets = g_list_first(g_list_prepend(item_to_widgets, ret));
00166     g_assert(item_to_widgets != NULL);
00167 
00168     return ret;
00169 }
00170 
00171 static GList * item_to_widget_retrieve_viewers(item * op) {
00172     return item_to_widget_retrieve(op)->viewers;
00173 }
00174 
00175 static void item_to_widget_store(item * op, inventory_viewer * view) {
00176     item_delete * x;
00177 
00178     x = item_to_widget_retrieve(op);
00179 
00180     if (g_list_find(x->viewers, view) == NULL) {
00181         x->viewers = g_list_prepend(x->viewers, view);
00182     }
00183     g_assert(x->viewers != NULL);
00184 
00185 
00186     /* If it's animated, also stick it on the shortlist of animated items. */
00187     if (op->animation_id > 0 && op->anim_speed) {
00188         /* Only stick it on if it's not already present. :S */
00189         if (g_list_find(animated_items, op) == NULL) {
00190             animated_items = g_list_first(g_list_prepend(animated_items, op));
00191             g_assert(animated_items != NULL);
00192         }
00193     }
00194 }
00195 
00196 /*
00197  * Remove
00198  */
00199 
00200 static void remove_widget_one(gpointer item_and_widget_x, gpointer view_x) {
00201     item_delete * item_and_widgets = (item_delete *)item_and_widget_x;
00202 
00203     item_and_widgets->viewers = g_list_remove(item_and_widgets->viewers, view_x);
00204 }
00205 
00206 static void item_to_widget_remove_widget(inventory_viewer * view) {
00207     g_list_foreach(item_to_widgets, remove_widget_one, view);
00208 }
00209 
00210 static void item_to_widget_remove_item(item * const op) {
00211     item * op_mangled = op;
00212     GList * search_return = NULL;
00213 
00214     if (item_to_widgets != NULL) {
00215         GList * victim_link = NULL;
00216         GList * i = NULL;
00217         item_delete * victim = NULL;
00218 
00219         /* Look for the item_delete for this item. */
00220         for (i = item_to_widgets; i != NULL; i = g_list_next(i))
00221         {
00222             item_delete * x = (item_delete *)(i->data);
00223             if (x->it == op) {
00224                 victim = x;
00225                 victim_link = i;
00226                 break;
00227             }
00228         }
00229 
00230         if (victim != NULL) {
00231             g_assert(victim_link != NULL);
00232 
00233             /* Remove the item_delete; free the widget-list first. */
00234             g_list_free(victim->viewers);
00235             item_to_widgets = g_list_remove_link(item_to_widgets, victim_link);
00236         }
00237     }
00238 
00239     /* Also nuke it from the animation list. (Hope g_list doesn't choke if it's not there.) */
00240     /*LOG(LOG_INFO, "inventory::item_to_widget_remove_item",
00241         "removing %d (%s) %p", op->tag, op->d_name, op);*/
00242     animated_items = g_list_remove(animated_items, op_mangled);
00243     search_return = g_list_find(animated_items, op);
00244     g_assert(search_return == NULL);
00245 }
00246 
00247 /*
00248  * Animate
00249  */
00250 
00251 static void animate_item(gpointer view_x, gpointer item_x) {
00252     item * it = (item *)item_x;
00253     PixmapInfo * new_face = pixmaps[it->face];
00254     inventory_viewer * view = (inventory_viewer *) view_x;
00255 
00256     /* LOG(LOG_INFO, "inventory::animate_item", "Called"); */
00257 
00258     /* Don't update views that are going to be completely reconstructed anyway. */
00259     if (view->complete_rebuild) return;
00260 
00261     gtk_clist_set_pixmap(GTK_CLIST(view->list),
00262         gtk_clist_find_row_from_data(GTK_CLIST(view->list), item_x), 0,
00263         (GdkPixmap*)new_face->icon_image,
00264         (GdkBitmap*)new_face->icon_mask);
00265 }
00266 
00267 static void animate_one_item(gpointer item_x, gpointer ignored) {
00268     item * it = (item *)item_x;
00269     GList * views = item_to_widget_retrieve_viewers(it);
00270 
00271     /* Is it animated? */
00272     g_assert(it->animation_id > 0 && it->anim_speed);
00273 
00274     it->last_anim++;
00275 
00276     /* Is it time to change the face yet? */
00277     if (it->last_anim < it->anim_speed) return;
00278 
00279     it->anim_state++;
00280 
00281     if (it->anim_state >= animations[it->animation_id].num_animations) {
00282       it->anim_state=0;
00283     }
00284     it->face = animations[it->animation_id].faces[it->anim_state];
00285     it->last_anim=0;
00286 
00287     /*LOG(LOG_INFO, "inventory::animate_one_item", "Animating %p: %s to %d", op, op->d_name, op->face);  */
00288 
00289     /* For each view the newly-updated item appears in, change its face. */
00290     g_list_foreach(views, animate_item, it);
00291 }
00292 
00293 static void animate_items(void) {
00294     g_list_foreach(animated_items, animate_one_item, NULL);
00295 }
00296 
00297 
00298 static void item_changed_anim_hook(item * op) {
00299     /* HACK Make sure its presence or absence in the animated-items list is correct. */
00300 
00301     if (op->animation_id > 0 && op->anim_speed) {
00302         if (g_list_find(animated_items, op) == NULL) {
00303             animated_items = g_list_prepend(animated_items, op);
00304         }
00305     } else {
00306         animated_items = g_list_remove(animated_items, op);
00307     }
00308 }
00309 
00310 
00311 /* -------------------------------------------------------------------------------------- */
00312 
00313 /*
00314  * (Re)building
00315  */
00316 
00317 static void highlight_item(GtkWidget * list, item * it, gint row) {
00318     extern GdkColor root_color[16]; /* gx11.c; it'll probably change when Lally finishes his patch... */
00319 
00320     if (it->cursed || it->damned) {
00321         if (!it->magical) {
00322             gtk_clist_set_background (GTK_CLIST(list), row,
00323                 &root_color[NDI_RED]);
00324         } else {
00325             gtk_clist_set_background (GTK_CLIST(list), row,
00326                 &root_color[NDI_NAVY]);
00327         }
00328     }
00329     else if (it->magical) {
00330         gtk_clist_set_background (GTK_CLIST(list), row,
00331             &root_color[NDI_BLUE]);
00332     }
00333 }
00334 
00335 #define FMT_WEIGHT(buf, buf_size, it) snprintf(buf, buf_size, "%6.1f" , it->nrof * it->weight)
00336 
00337 static void rebuild_our_widget(inventory_viewer * view) {
00338     item * it;
00339     char buffer[3][MAX_BUF];
00340     char *columns[3];
00341     gfloat scrollbar_pos; /* Copying from gx11.c, etc etc. */
00342     GtkWidget * scroll_window = NULL;
00343     GtkWidget * list = NULL;
00344     uint16 mh = image_size, mw = image_size;
00345 
00346     g_assert(view != NULL);
00347     g_assert(view->complete_rebuild);
00348 
00349     scroll_window = view->scroll_window;
00350     list = view->list;
00351 
00352     /* GtkAdjustment doesn't give any indirect way of extracting that value. :( */
00353     scrollbar_pos =
00354       gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll_window))->value;
00355     gtk_clist_freeze(GTK_CLIST(list));
00356     gtk_clist_clear(GTK_CLIST(list));
00357 
00358     columns[0] = buffer[0];
00359     columns[1] = buffer[1];
00360 
00361     for (it = view->cont->inv; it != NULL; it = it->next) {
00362         PixmapInfo * pixmap = pixmaps[it->face];
00363         gint row;
00364 
00365         if (!view->shows(it)) continue;
00366 
00367         if (view->face_column_resizes) {
00368             if (pixmap->icon_width > mw) mw = pixmap->icon_width;
00369             if (pixmap->icon_height > mh) mh = pixmap->icon_height;
00370         }
00371 
00372         /* TODO safe_strcat! Perhaps use glib's string functions? */
00373         strcpy (buffer[0]," ");
00374         strcpy (buffer[1], it->d_name);
00375 
00376         if (view->show_flags) {
00377             strcat (buffer[1], it->flags);
00378         }
00379 
00380         if (view->show_weight && !(it->weight < 0)) {
00381             FMT_WEIGHT(buffer[2], MAX_BUF, it);
00382             columns[2] = buffer[2];
00383         } else {
00384             columns[2] = " ";
00385         }
00386 
00387         row = gtk_clist_append(GTK_CLIST(list), columns);
00388 
00389         /* Set original pixmap */
00390         gtk_clist_set_pixmap (GTK_CLIST (list), row, 0,
00391                         (GdkPixmap*)pixmap->icon_image,
00392                         (GdkBitmap*)pixmap->icon_mask);
00393 
00394         gtk_clist_set_row_data (GTK_CLIST(list), row, it);
00395 
00396         item_to_widget_store(it, view);
00397 
00398         if (view->highlight) {
00399             highlight_item(list, it, row);
00400         }
00401     }
00402 
00403     if (view->face_column_resizes) {
00404         if (view->image_width != mw) {
00405             gtk_clist_set_column_width(GTK_CLIST(list), 0, mw);
00406             view->image_width = mw;
00407         }
00408         if (view->image_height != mh) {
00409             gtk_clist_set_row_height(GTK_CLIST(list), mh);
00410             view->image_height = mh;
00411         }
00412     }
00413 
00414     /* Ok, stuff is drawn, now replace the scrollbar positioning as far as possible */
00415     gtk_adjustment_set_value(
00416         GTK_ADJUSTMENT(
00417           gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll_window))
00418         ),
00419         scrollbar_pos);
00420     gtk_clist_thaw(GTK_CLIST(list));
00421 
00422     view->complete_rebuild = FALSE;
00423 }
00424 
00425 /*
00426  * Updates and animation
00427  */
00428 
00429 /* forward */
00430 static bool view_visible(inventory_viewer * view);
00431 
00432 static void item_tick_per_view(gpointer data, gpointer user_data_ignored) {
00433     inventory_viewer * view;
00434     view = (inventory_viewer *)data;
00435 
00436     if (redraw_needed) {
00437         /*LOG(LOG_INFO, "inventory::item_tick_per_view", "%p redraw_needed", view); */
00438 
00439         /* Faces have changed (gtk/image.c). Sadly, cache.c isn't more granular than this, so we
00440         can only update *all* the faces. */
00441 
00442         /* TODO If the inventory isn't otherwise dirty, only cycle through the faces.
00443         For the moment, rebuild the entire list. */
00444         view->complete_rebuild = TRUE;
00445 
00446         /* Do not clear the flag; that's done in gx11.c::do_timeout().
00447         In any case, we'd smash it for the other views. :S */
00448     }
00449 
00450     /*
00451      * HACK
00452      * If visible() ever returns false, remember to manually update the widget when
00453      * it becomes visible!
00454      */
00455     if (!view_visible(view)) return;
00456 
00457     if (view->complete_rebuild) {
00458         /*LOG(LOG_INFO, "inventory::item_tick_per_view", "rebuild %p on timeout", view);*/
00459         rebuild_our_widget(view);
00460     }
00461 }
00462 
00463 static void itemview_tick(void) {
00464     animate_items();
00465 
00466     g_list_foreach(views, item_tick_per_view, NULL);
00467 }
00468 
00469 /* TODO Another optimization; if an item is new (to the container),
00470 we only need to dirty the views that will show the item. */
00471 
00472 static void item_changed_one(gpointer view_x, gpointer op_x) {
00473     inventory_viewer * view = (inventory_viewer *)view_x;
00474     item * it = (item *)op_x;
00475 
00476     /* TODO Finer-grained checking, so only the affected panels
00477     complete_rebuild, and for *big* fenceposting, only add a row. */
00478 
00479     if (view->cont == it->env) {
00480         /* TODO My brother says he can do better. */
00481         view->complete_rebuild = TRUE;
00482         /*LOG(LOG_INFO, "inventory::item_changed_one", "%p dirtied", view); */
00483     } else {
00484         /*LOG(LOG_INFO, "inventory::item_changed_one", "%p not container", view); */
00485     }
00486 
00487 }
00488 
00489 void item_event_item_changed(item * op) {
00490     item_changed_anim_hook(op);
00491 
00492     /*LOG(LOG_INFO, "inventory::item_event_item_changed", "Changed: %d %s %d", op->tag, op->d_name, op->face); */
00493     g_list_foreach(views, item_changed_one, (gpointer)op);
00494 
00495     /* Update inventory list for spell writing. */
00496     if (can_write_spell_on(op) && spellinventory != NULL && GTK_WIDGET_VISIBLE(spellinventory)) {
00497         gint row = gtk_clist_find_row_from_data(GTK_CLIST(spellinventory), op);
00498         if (row != -1)
00499             gtk_clist_set_text(GTK_CLIST(spellinventory), row, 1, op->d_name);
00500     }
00501 
00502 }
00503 
00504 
00505 
00506 static void container_clearing_one(gpointer view_x, gpointer op_x) {
00507     inventory_viewer * view = (inventory_viewer *)view_x;
00508     item * it = (item *)op_x;
00509 
00510     /* TODO We'd have to tweak for recursive-search in a tree widget, maybe. */
00511     if (view->cont == it) {
00512         if (!view->complete_rebuild) {
00513             view->complete_rebuild = TRUE;
00514             /* Wonder if at any later stage pass view? */
00515             item_to_widget_remove_widget(view);
00516             /*LOG(LOG_INFO, "inventory::container_clearing_one", "%p dirtied", view);*/
00517         } else {
00518             /*LOG(LOG_INFO, "inventory::container_clearing_one", "%p already dirty", view);*/
00519         }
00520     } else {
00521         /*LOG(LOG_INFO, "inventory::container_clearing_one", "%p not container", view);*/
00522     }
00523 }
00524 
00525 void item_event_container_clearing(item * op) {
00526     /*LOG(LOG_INFO, "inventory::item_event_container_clearing", "Clearing: %d %s %d", op->tag, op->d_name, op->face); */
00527     g_list_foreach(views, container_clearing_one, (gpointer)op);
00528 }
00529 
00530 static void item_deleting_one(gpointer view_x, gpointer op_x) {
00531     inventory_viewer * view = (inventory_viewer *)view_x;
00532     item * it = (item *)op_x;
00533 
00534     if (it->env != view->cont) {
00535         /*LOG(LOG_INFO, "inventory::item_deleting_one", "%p not container", view);*/
00536         return;
00537     }
00538 
00539     if (view->complete_rebuild) {
00540         /*LOG(LOG_INFO, "inventory::item_deleting_one", "%p already dirty", view);*/
00541         return;
00542     }
00543 
00544     /*LOG(LOG_INFO, "inventory::item_deleting_one", "%p removing row", view);*/
00545 
00546     if (view->face_column_resizes) {
00547         PixmapInfo * it_face = pixmaps[it->face];
00548 
00549         /* Special handling to shrink the image column if the 'responsible' face vanishes. */
00550 
00551         if (it_face->icon_width == image_size && it_face->icon_height == image_size) {
00552             ; /* The column will never get smaller than image_size. */
00553         } else if (it_face->icon_width < view->image_width
00554             && it_face->icon_height < view->image_height) {
00555             ; /* This face isn't a cause of either of the maximums. */
00556         } else {
00557             PixmapInfo * tmp_face;
00558             item * tmp_item;
00559             uint16 mw = image_size, mh = image_size;
00560 
00561             /* TODO Refactor with rebuild_our_widget. */
00562 
00563             /* it_face requires one of the dimensions to be that large.
00564             See if removing it changes the requirements. */
00565 
00566             for (tmp_item = view->cont->inv; tmp_item != NULL; tmp_item = tmp_item->next) {
00567                 if (tmp_item == it) continue;
00568 
00569                 if (!view->shows(it)) continue;
00570 
00571                 tmp_face = pixmaps[tmp_item->face];
00572 
00573                 if (tmp_face->icon_width > mw) mw = tmp_face->icon_width;
00574                 if (tmp_face->icon_height > mh) mh = tmp_face->icon_height;
00575             }
00576 
00577             /* mw, mh hold the size requirement for every shown item bar op. */
00578 
00579             if (view->image_width != mw) {
00580                 gtk_clist_set_column_width(GTK_CLIST(view->list), 0, mw);
00581                 view->image_width = mw;
00582             }
00583             if (view->image_height != mh) {
00584                 gtk_clist_set_row_height(GTK_CLIST(view->list), mh);
00585                 view->image_height = mh;
00586             }
00587         }
00588     }
00589 
00590     /* Remove the row containing the item. */
00591     gtk_clist_remove(GTK_CLIST(view->list),
00592         gtk_clist_find_row_from_data(GTK_CLIST(view->list), op_x)
00593     );
00594 }
00595 
00596 void item_event_item_deleting(item * op) {
00597     /*LOG(LOG_INFO, "inventory::item_event_item_deleting", "Deleting: %d %s %d", op->tag, op->d_name, op->face); */
00598     g_list_foreach(views, item_deleting_one, (gpointer)op);
00599     /* Among other things, prevent animating the now-Missing item. */
00600     item_to_widget_remove_item(op);
00601 
00602     if (can_write_spell_on(op) && spellinventory != NULL && GTK_WIDGET_VISIBLE(spellinventory)) {
00603         gint row = gtk_clist_find_row_from_data(GTK_CLIST(spellinventory), op);
00604         if (row != -1)
00605             gtk_clist_remove(GTK_CLIST(spellinventory), row);
00606     }
00607 }
00608 
00609 
00610 /*
00611  * Configuration
00612  */
00613 
00614 /* *Could* do these without rebuilding widget, but they almost never happen,
00615    so not a problem? */
00616 
00617 static void inventory_viewer_set_show_weight(inventory_viewer * view, bool show_weight) {
00618     if (view->show_weight == show_weight) {
00619         return;
00620     }
00621 
00622     view->complete_rebuild = TRUE;
00623     view->show_weight = show_weight;
00624 }
00625 
00626 static void inventory_viewer_set_highlight(inventory_viewer * view, bool highlight) {
00627     if (view->highlight == highlight) {
00628         return;
00629     }
00630 
00631     view->complete_rebuild = TRUE;
00632     view->highlight = highlight;
00633 }
00634 
00635 static void inventory_viewer_set_show_flags(inventory_viewer * view, bool show_flags) {
00636     if (view->show_flags == show_flags) {
00637         return;
00638     }
00639 
00640     view->complete_rebuild = TRUE;
00641     view->show_flags = show_flags;
00642 }
00643 
00644 static void inventory_viewer_set_container(inventory_viewer * view, item * new_cont) {
00645     if (view->cont == new_cont) return;
00646 
00647     /*LOG(LOG_INFO, "inventory::inventory_viewer_set_container", "%p dirtied", view);*/
00648     view->cont = new_cont;
00649     view->complete_rebuild = TRUE;
00650 }
00651 
00652 
00653 /*
00654  * Handle mouse presses in the lists
00655  */
00656 
00657 #include "gtkproto.h" /* draw_info */
00658 static void list_button_event(
00659     GtkWidget *gtklist,
00660     gint row, gint column,
00661     GdkEventButton *event,
00662     inventory_viewer * view)
00663 {
00664     item *it;
00665     it = gtk_clist_get_row_data (GTK_CLIST(gtklist), row);
00666     gtk_clist_unselect_row (GTK_CLIST(gtklist), row, 0);
00667 
00668     if (event->button==1) {
00669         if (event->state & GDK_SHIFT_MASK)
00670           toggle_locked(it);
00671         else
00672           client_send_examine (it->tag);
00673 
00674     }
00675     if (event->button==2) {
00676         if (event->state & GDK_SHIFT_MASK)
00677           send_mark_obj(it);
00678         else
00679           client_send_apply (it->tag);
00680     }
00681     if (event->button==3) {
00682         if (it->locked) {
00683             draw_info ("This item is locked. To drop it, first unlock by shift+leftclicking on it.",
00684                 NDI_BLACK);
00685         } else {
00686             cpl.count = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(counttext));
00687             client_send_move (view->move_dest->tag, it->tag, cpl.count);
00688             if (!use_config[CONFIG_POPUPS]) { /* TODO I see no popping up here? */
00689                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(counttext),0.0);
00690                 cpl.count=0;
00691             }
00692         }
00693     }
00694 }
00695 
00696 
00697 
00698 /****************************************************************************
00699  *
00700  * Everything below is specific to the inventory and look windows; everything
00701  * above should be general.
00702  *
00703  * If you want to change, or especially *add*, inventory viewers, you should
00704  * only need to modify things below this comment.
00705  *
00706  * (Viewing multiple *containers* possibly requires changes in
00707  * common and the *server*...)
00708  *
00709  ****************************************************************************/
00710 
00711 /*
00712  * Creation (and destruction)
00713  */
00714 
00715 #include "pixmaps/all.xpm"
00716 #include "pixmaps/hand.xpm"
00717 #include "pixmaps/hand2.xpm"
00718 #include "pixmaps/coin.xpm"
00719 #include "pixmaps/skull.xpm"
00720 #include "pixmaps/mag.xpm"
00721 #include "pixmaps/nonmag.xpm"
00722 #include "pixmaps/lock.xpm"
00723 #include "pixmaps/unlock.xpm"
00724 
00725 static bool show_all(item * ignored) { return TRUE; }
00726 static bool show_applied(item * it) { return it->applied; }
00727 static bool show_unapplied(item * it) { return !(it->applied); }
00728 static bool show_unpaid(item * it) { return it->unpaid; }
00729 static bool show_cursed(item * it) { return it->cursed || it->damned; }
00730 static bool show_magical(item * it) { return it->magical; }
00731 static bool show_nonmagical(item * it) { return !(it->magical); }
00732 static bool show_locked(item * it) { return it->locked; }
00733 static bool show_unlocked(item * it) { return !(it->locked); }
00734 
00735 typedef struct {
00736   const char *name;
00737   const char *const *xpm;
00738   itemfilter filter;
00739   bool highlight;
00740 } fixed_tab_init;
00741 
00742 /* TODO Dynamic!!!. */
00743 #define TYPE_LISTS 9
00744 
00745 /* These are used to create the inventory tabs. */
00746 static fixed_tab_init fixed_tabs[TYPE_LISTS] = {
00747   { "all", all_xpm, show_all, TRUE },
00748   { "applied", hand_xpm, show_applied, FALSE },
00749   { "unapplied", hand2_xpm, show_unapplied, FALSE },
00750   { "unpaid", coin_xpm, show_unpaid, FALSE },
00751   { "cursed", skull_xpm, show_cursed, FALSE },
00752   { "magical", mag_xpm, show_magical, FALSE },
00753   { "nonmagical", nonmag_xpm, show_nonmagical, FALSE },
00754   { "locked", lock_xpm, show_locked, FALSE },
00755   { "unlocked", unlock_xpm, show_unlocked, TRUE }
00756 };
00757 
00758 /* TODO Maybe I should move these two into the itemlist structs? */
00759 static GList * inv_viewers = NULL;
00760 static inventory_viewer * look_viewer = NULL;
00761 /* The inventory_viewers created from the entries above that highlight,
00762 plus look_viewer. */
00763 static GList * highlit_inv_viewers = NULL;
00764 
00765 static GtkWidget * look_widget = NULL;
00766 static GtkWidget * inv_notebook = NULL;
00767 
00768 
00769 
00770 /*
00771  * Destroy the current views when the client is toggled between splitwindow and onewindow
00772  * mode (or vice versa).
00773  */
00774 
00775 static void add_removal_victim(gpointer view_x, gpointer victim_views_p_x) {
00776     inventory_viewer * view = (inventory_viewer *)view_x;
00777     GList ** victim_views_p = (GList **) victim_views_p_x;
00778     if (g_list_find(*victim_views_p, view) == NULL) {
00779         *victim_views_p = g_list_prepend(*victim_views_p, view);
00780     }
00781 }
00782 
00783 static void nuke_view(gpointer view_x, gpointer notused) {
00784     inventory_viewer * view = (inventory_viewer *)view_x;
00785     views = g_list_remove(views, view);
00786     item_to_widget_remove_widget(view);
00787     free(view);
00788 }
00789 
00790 void inventory_splitwin_toggling(void) {
00791     GList * victim_views = NULL;
00792 
00793     /* Eeek! Need to throw away all sorts of things. */
00794 
00795     /* We need to get of everything in inv_viewers, everything
00796     in highlit_inv_viewers, and the look_viewer; however, we also
00797     need to free them exactly once. add_removal_victim effectively
00798     creates a set.*/
00799     g_list_foreach(inv_viewers, add_removal_victim, &victim_views);
00800     g_list_foreach(highlit_inv_viewers, add_removal_victim, &victim_views);
00801     add_removal_victim(look_viewer, &victim_views);
00802 
00803     /* Free the views. */
00804     g_list_foreach(victim_views, nuke_view, NULL);
00805     g_list_free(victim_views);
00806 
00807     /* Zero the values; it's like the client just started. */
00808     /* We presume widgets'll be taken care of by GTK widget-destroy functions. */
00809     look_viewer = NULL;
00810     look_widget = NULL;
00811     inv_viewers = NULL;
00812     inv_notebook = NULL;
00813     highlit_inv_viewers = NULL;
00814 }
00815 
00816 /*
00817  * Resizing all the columns when the widget's size is changed.
00818  */
00819 
00820 static void resize_left_widget_one(gpointer view_x, gpointer total_width_x) {
00821     inventory_viewer  * view = (inventory_viewer *)view_x;
00822     gint total_width = GPOINTER_TO_INT(total_width_x);
00823 
00824     if (view == NULL) {
00825         /* Weird. This is never set as a signal handler, but somehow gtk1.2
00826            decides to call this with a NULL view when going to split windows.
00827         */
00828         return;
00829     }
00830 
00831     gtk_clist_set_column_width(GTK_CLIST(view->list),
00832         1, total_width - view->image_width);
00833 
00834     /*LOG(LOG_INFO, "inventory::resize_left_widget_one", "view %p image_width %d", view, view->image_width);*/
00835 }
00836 
00837 /* I mean, the minimum width that won't cause infinite recursion is dependent on the
00838 width of the scrollbar and possibly the font used in the title widgets; all that could
00839 change if you switch GTK-engine-thingy, so hard-coding this value is silly. On the other
00840 hand, I don't know any alternative, aside from using clist's automatic sizing features.
00841 
00842 ... and why in ding dong would making it smaller (70) *add* the horizontal scrollbar?
00843 */
00844 #define MAGIC_SAFE_WIDTH 75
00845 
00846 static void resize_left_widgets(GtkWidget *widget, GtkAllocation *event) {
00847     static gint old_total_width = 0;
00848     inventory_viewer * hack;
00849     gint total_width;
00850 
00851     /* If GTK can unexpectedly call resize_left_widget_one() as a signal
00852        handler when the inventory widgets temporarily don't exist, then we
00853        might as well watch our tail here, too. */
00854     if (inv_viewers == NULL) return;
00855     if (look_viewer == NULL) return;
00856 
00857     /* HACK Extract the first inventory-viewer. */
00858     hack = (inventory_viewer *)(inv_viewers->data);
00859     total_width = GTK_CLIST(hack->list)->clist_window_width - MAGIC_SAFE_WIDTH;
00860 
00861     if (old_total_width == total_width) return;
00862     old_total_width = total_width;
00863 
00864     g_list_foreach(inv_viewers, resize_left_widget_one, GINT_TO_POINTER(total_width));
00865     resize_left_widget_one(look_viewer, GINT_TO_POINTER(total_width));
00866 }
00867 
00868 
00869 
00870 /*
00871  * Redrawing only the active inventory view on idle-tick..
00872  */
00873 
00874 /* HACK Only the current inventory tab is rebuilt on each tick. */
00875 static bool view_visible(inventory_viewer * view) {
00876     GList * i;
00877 
00878     /* Bottom widget. */
00879     if (view == look_viewer) return TRUE;
00880 
00881     /* If it's an inv_viewer, if its the visible notebook page. */
00882     for(i = inv_viewers; i != NULL; i = g_list_next(i)) {
00883         if (i->data == view) {
00884             return view->scroll_window == gtk_notebook_get_nth_page(
00885                 GTK_NOTEBOOK(inv_notebook),
00886                 gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook)));
00887         }
00888     }
00889 
00890     /* assume */
00891     return TRUE;
00892 }
00893 
00894 /* As the wossname of the above, when the visible tab is changed, rebuild it if needed.
00895 Tied to widgets by mod_one_widget(). */
00896 static void redraw_on_show(GtkWidget * a, GdkEventVisibility * event, gpointer view_x) {
00897     inventory_viewer * view = (inventory_viewer *)view_x;
00898 
00899     if (!view->complete_rebuild) return;
00900 
00901     /*{
00902         char * x = "unexpected";
00903 
00904         if (event->state == GDK_VISIBILITY_UNOBSCURED) {
00905             x = "unobscured";
00906         } else if (event->state == GDK_VISIBILITY_PARTIAL) {
00907             x = "partial";
00908         } else if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) {
00909             x = "obscured";
00910         }
00911 
00912         LOG(LOG_INFO, "inventory::redraw_on_show", "rebuilding %p (visibility changed to %s)", view, x);
00913     }*/
00914 
00915     rebuild_our_widget(view);
00916 }
00917 
00918 
00919 
00920 static GtkWidget * get_inv_widget(void) {
00921     fixed_tab_init * i;
00922     GtkStyle *tabstyle;
00923     GdkPixmap *labelgdkpixmap;
00924     GdkBitmap *labelgdkmask;
00925     GtkWidget *tablabel;
00926     inventory_viewer * view;
00927 
00928     if (inv_notebook != NULL) {
00929        return inv_notebook;
00930     }
00931 
00932     inv_notebook = gtk_notebook_new();
00933     gtk_notebook_set_tab_pos (GTK_NOTEBOOK (inv_notebook), GTK_POS_TOP );
00934 
00935     for (i = fixed_tabs; i - fixed_tabs < TYPE_LISTS; i++) {
00936         tabstyle = gtk_widget_get_style(gtkwin_root);
00937 
00938         labelgdkpixmap = gdk_pixmap_create_from_xpm_d(
00939             gtkwin_root->window,
00940             &labelgdkmask,
00941             &tabstyle->bg[GTK_STATE_NORMAL],
00942             (gchar **)  i->xpm );
00943 
00944         tablabel = gtk_pixmap_new (labelgdkpixmap, labelgdkmask);
00945         gtk_widget_show (tablabel);
00946 
00947         view = new_inventory_viewer(cpl.ob, i->filter, cpl.below); /* player to ground */
00948         view->face_column_resizes = FALSE;
00949 
00950         inventory_viewer_set_highlight(view, i->highlight);
00951         highlit_inv_viewers = g_list_append(highlit_inv_viewers, view);
00952 
00953         inv_viewers = g_list_append(inv_viewers, view);
00954 
00955         gtk_notebook_append_page (GTK_NOTEBOOK (inv_notebook),
00956             view->scroll_window,
00957             tablabel);
00958 
00959         /*
00960          * Attach events to make some extra behaviours...
00961          */
00962 
00963         gtk_signal_connect(GTK_OBJECT(view->list),
00964             "size-allocate",
00965             (GtkSignalFunc)resize_left_widgets,
00966             NULL);
00967 
00968         /* Since the program will automatically adjust these, any changes
00969          * the user makes can get obliterated, so just don't let the user
00970          * make changes.
00971          */
00972         gtk_clist_set_column_resizeable(GTK_CLIST(view->list), 0, FALSE);
00973         gtk_clist_set_column_resizeable(GTK_CLIST(view->list), 1, FALSE);
00974         gtk_clist_set_column_resizeable(GTK_CLIST(view->list), 2, FALSE);
00975 
00976 
00977         /* Only the visible tab redraws on inventory_tick(); redraw_on_show
00978         redraws dirty tabs when they *become* visible. */
00979         gtk_signal_connect(GTK_OBJECT(view->list),
00980             "visibility-notify-event",
00981             (GtkSignalFunc)redraw_on_show,
00982             view);
00983 
00984          gtk_widget_add_events(view->list, GDK_VISIBILITY_NOTIFY_MASK);
00985     }
00986 
00987     gtk_widget_show(inv_notebook);
00988 
00989     return inv_notebook;
00990 }
00991 
00992 
00993 
00994 static GtkWidget *get_look_widget(void) {
00995     if (look_widget != NULL) {
00996         return look_widget;
00997     }
00998 
00999     look_viewer = new_inventory_viewer(cpl.below, show_all, cpl.ob); /* ground to player */
01000     look_viewer->highlight = TRUE;
01001     look_viewer->face_column_resizes = TRUE;
01002     highlit_inv_viewers = g_list_append(highlit_inv_viewers, look_viewer);
01003 
01004     look_widget = look_viewer->scroll_window;
01005 
01006     return look_widget;
01007 }
01008 
01009 /*
01010  * Now slap the labels on around the invwidgets.
01011  */
01012 
01013 itemlist look_list, inv_list;
01014 
01015 GtkWidget *closebutton;
01016 
01017 /* forward */
01018 static void close_container_callback(item *op);
01019 
01020 
01021 void get_look_display(GtkWidget *frame)
01022 {
01023   GtkWidget *vbox1;
01024   GtkWidget *hbox1;
01025 
01026   look_list.env = cpl.below;
01027   strcpy (look_list.title, "You see:");
01028   strcpy (look_list.last_title, look_list.title);
01029   strcpy (look_list.last_weight, "0");
01030   strcpy (look_list.last_maxweight, "0");
01031   look_list.show_weight = TRUE;
01032   look_list.weight_limit = 0;
01033 
01034 
01035   vbox1 = gtk_vbox_new(FALSE, 0);/*separation here*/
01036   gtk_container_add (GTK_CONTAINER(frame), vbox1);
01037 
01038   hbox1 = gtk_hbox_new(FALSE, 2);
01039   gtk_box_pack_start (GTK_BOX(vbox1),hbox1, FALSE, FALSE, 0);
01040   gtk_widget_show (hbox1);
01041 
01042   closebutton = gtk_button_new_with_label ("Close");
01043   gtk_signal_connect_object (GTK_OBJECT (closebutton), "clicked",
01044                                GTK_SIGNAL_FUNC(close_container_callback),
01045                                NULL);
01046   gtk_widget_set_sensitive(closebutton, FALSE);
01047   gtk_box_pack_start (GTK_BOX(hbox1),closebutton, FALSE, FALSE, 2);
01048   gtk_widget_show (closebutton);
01049   gtk_tooltips_set_tip (tooltips, closebutton,
01050       "This will close an item if you have one open.",
01051       NULL);
01052 
01053   look_list.label = gtk_label_new ("You see:");
01054   gtk_box_pack_start (GTK_BOX(hbox1),look_list.label, TRUE, FALSE, 2);
01055   gtk_widget_show (look_list.label);
01056 
01057   look_list.weightlabel = gtk_label_new ("0");
01058   gtk_box_pack_start (GTK_BOX(hbox1),look_list.weightlabel, TRUE, FALSE, 2);
01059   gtk_widget_show (look_list.weightlabel);
01060 
01061   look_list.maxweightlabel = gtk_label_new ("0");
01062   gtk_box_pack_start (GTK_BOX(hbox1),look_list.maxweightlabel, TRUE, FALSE, 2);
01063   gtk_widget_show (look_list.maxweightlabel);
01064 
01065   gtk_box_pack_start(GTK_BOX(vbox1), get_look_widget(), TRUE, TRUE, 0);
01066 
01067   gtk_widget_show (vbox1);
01068 }
01069 
01070 /* Used for getting and dropping more than 1 of an item, and for dimension door
01071    and jumping lengths. */
01072 GtkWidget *counttext;
01073 
01074 static void count_callback(GtkWidget *widget, GtkWidget *entry)
01075 {
01076     const gchar *count_text;
01077     extern GtkWidget * gtkwin_info_text;
01078 
01079     count_text = gtk_entry_get_text(GTK_ENTRY(counttext));
01080     cpl.count = atoi (count_text);
01081     gtk_widget_grab_focus (GTK_WIDGET(gtkwin_info_text)); /* I wonder why this isn't entrytext? */
01082 }
01083 
01084 void get_inv_display(GtkWidget *frame)
01085 {
01086   GtkWidget *vbox2;
01087   GtkWidget *hbox1;
01088   GtkWidget *invlabel;
01089   GtkAdjustment *adj;
01090 
01091   strcpy (inv_list.title, "Inventory:");
01092   strcpy (inv_list.last_title, inv_list.title);
01093   strcpy (inv_list.last_weight, "0");
01094   strcpy (inv_list.last_maxweight, "0");
01095   inv_list.env = cpl.ob;
01096   inv_list.show_weight = TRUE;
01097   inv_list.weight_limit = 0;
01098 
01099   vbox2 = gtk_vbox_new(FALSE, 0); /* separation here */
01100 
01101   gtk_container_add (GTK_CONTAINER(frame), vbox2);
01102 
01103   hbox1 = gtk_hbox_new(FALSE, 2);
01104   gtk_box_pack_start (GTK_BOX(vbox2),hbox1, FALSE, FALSE, 0);
01105   gtk_widget_show (hbox1);
01106 
01107 
01108   inv_list.label = gtk_label_new ("Inventory:");
01109   gtk_box_pack_start (GTK_BOX(hbox1),inv_list.label, TRUE, FALSE, 2);
01110   gtk_widget_show (inv_list.label);
01111 
01112   inv_list.weightlabel = gtk_label_new ("0");
01113   gtk_box_pack_start (GTK_BOX(hbox1),inv_list.weightlabel, TRUE, FALSE, 2);
01114   gtk_widget_show (inv_list.weightlabel);
01115 
01116 
01117   inv_list.maxweightlabel = gtk_label_new ("0");
01118   gtk_box_pack_start (GTK_BOX(hbox1),inv_list.maxweightlabel, TRUE, FALSE, 2);
01119   gtk_widget_show (inv_list.maxweightlabel);
01120 
01121   invlabel = gtk_label_new ("Count:");
01122   gtk_box_pack_start (GTK_BOX(hbox1),invlabel, FALSE, FALSE, 5);
01123   gtk_widget_show (invlabel);
01124 
01125   adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 100000.0, 1.0,
01126                                                   100.0, 0.0);
01127   counttext = gtk_spin_button_new (adj, 1.0, 0);
01128 
01129   gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (counttext), FALSE);
01130   gtk_widget_set_usize (counttext, 65, 0);
01131   gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (counttext),
01132                                      GTK_UPDATE_ALWAYS);
01133    gtk_signal_connect(GTK_OBJECT(counttext), "activate",
01134                      GTK_SIGNAL_FUNC(count_callback),
01135                      counttext);
01136 
01137 
01138   gtk_box_pack_start (GTK_BOX (hbox1),counttext, FALSE, FALSE, 0);
01139 
01140   gtk_widget_show (counttext);
01141   gtk_tooltips_set_tip (tooltips, counttext,
01142       "This sets the number of items you wish to pickup or drop. You can also use the keys 0-9 to set it.",
01143       NULL);
01144 
01145   gtk_box_pack_start (GTK_BOX(vbox2), get_inv_widget(), TRUE, TRUE, 0);
01146   gtk_widget_show (vbox2);
01147 }
01148 
01149 
01150 
01151 /* commandline toggle tab */
01152 
01153 void command_show (const char *params) {
01154     int i;
01155 
01156     if(params == NULL)  {
01157         /* Shouldn't need to get current page, but next_page call is not wrapping
01158          * like the docs claim it should.
01159          */
01160         if (gtk_notebook_get_current_page(GTK_NOTEBOOK(inv_notebook))==TYPE_LISTS-1) {
01161             gtk_notebook_set_page(GTK_NOTEBOOK(inv_notebook), 0);
01162         } else {
01163             gtk_notebook_next_page(GTK_NOTEBOOK(inv_notebook));
01164         }
01165         return;
01166     }
01167 
01168     for (i = 0; i < TYPE_LISTS; i++) {
01169         /* Prefix match */
01170         if (!strncmp(params, fixed_tabs[i].name, strlen(params))) {
01171             gtk_notebook_set_page(GTK_NOTEBOOK(inv_notebook), i);
01172             return;
01173         }
01174     }
01175 }
01176 
01177 /*
01178  * Label updates.
01179  */
01180 
01181 
01182 void update_list_labels (itemlist *l)
01183 {
01184     char weight[MAX_BUF];
01185     char max_weight[MAX_BUF];
01186 
01187     /* draw title and put stuff in widgets */
01188 
01189     if ( strcmp( l->title, l->last_title ) ) {
01190         strcpy(l->last_title, l->title);
01191         strcpy(weight,l->title);
01192         gtk_label_set (GTK_LABEL(l->label), weight);
01193         gtk_widget_draw (l->label, NULL);
01194     }
01195 
01196     if(l->env->weight < 0 || !l->show_weight) {
01197             strcpy(weight, " ");
01198             strcpy(max_weight, " ");
01199     }
01200     else if (!l->weight_limit) {
01201             sprintf (weight, "%6.1f",l->env->weight);
01202         strcpy (max_weight, " ");
01203     } else {
01204             sprintf (weight, "%6.1f",l->env->weight);
01205             sprintf (max_weight, "/ %4d",l->weight_limit / 1000);
01206     }
01207 
01208     if ( strcmp( weight, l->last_weight ) ) {
01209         strcpy(l->last_weight, weight);
01210         gtk_label_set (GTK_LABEL(l->weightlabel), weight);
01211         gtk_widget_draw (l->weightlabel, NULL);
01212     }
01213     if ( strcmp( max_weight, l->last_maxweight ) ) {
01214         strcpy(l->last_maxweight, max_weight);
01215             gtk_label_set (GTK_LABEL(l->maxweightlabel), max_weight);
01216         gtk_widget_draw (l->maxweightlabel, NULL);
01217     }
01218 
01219     l->env->inv_updated = FALSE;
01220 }
01221 
01222 /*
01223  *  update_list_labels() redraws inventory and look window labels when necessary
01224  *
01225  *  (Maybe somewhat more often; look_list doesn't always have a weight shown, possibly.)
01226  */
01227 static void update_lists_labels(void)
01228 {
01229   if (inv_list.env->inv_updated) {
01230     update_list_labels (&inv_list);
01231   }
01232 
01233   if (look_list.env->inv_updated) {
01234     update_list_labels (&look_list);
01235   }
01236 
01237 }
01238 
01239 
01240 
01241 /*
01242  * Events
01243  */
01244 
01245 void set_weight_limit (uint32 wlim)
01246 {
01247     inv_list.weight_limit = wlim;
01248     update_list_labels(&inv_list);
01249 }
01250 
01251 /* toggle weight */
01252 
01253 static void set_show_weight_inv_one(gpointer view_x, gpointer new_setting) {
01254     inventory_viewer * view = (inventory_viewer *)view_x;
01255     inventory_viewer_set_show_weight(view, GPOINTER_TO_INT(new_setting));
01256 }
01257 
01258 void set_show_weight (const char *s)
01259 {
01260     if (s == NULL || *s == 0 || strncmp ("inventory", s, strlen(s)) == 0) {
01261         inv_list.show_weight = ! inv_list.show_weight; /* toggle */
01262         update_list_labels (&inv_list);
01263         g_list_foreach(inv_viewers, set_show_weight_inv_one, GINT_TO_POINTER((int)inv_list.show_weight));
01264     } else if (strncmp ("look", s, strlen(s)) == 0) {
01265         look_list.show_weight = ! look_list.show_weight; /* toggle */
01266         update_list_labels (&look_list);
01267         inventory_viewer_set_show_weight(look_viewer, look_list.show_weight);
01268     }
01269 }
01270 
01271 
01272 
01273 /* toggle flags */
01274 
01275 static void set_flags_one(gpointer view_x, gpointer show_flags) {
01276     inventory_viewer * view = (inventory_viewer *)view_x;
01277     inventory_viewer_set_show_flags(view, GPOINTER_TO_INT(show_flags));
01278 }
01279 
01280 static void set_inv_flags(bool show_flags) {
01281     g_list_foreach(inv_viewers, set_flags_one, GINT_TO_POINTER((int)show_flags));
01282 }
01283 
01284 static void set_look_flags(bool show_flags) {
01285     inventory_viewer_set_show_flags(look_viewer, show_flags);
01286 }
01287 
01288 void itemlist_set_show_icon(itemlist * l, int new_setting) {
01289    if (l->show_icon == new_setting) return;
01290 
01291    /* HACK */
01292    if (l == &inv_list) {
01293        set_inv_flags(!new_setting);
01294    } else if (l == &look_list) {
01295        set_look_flags(!new_setting);
01296    } else {
01297       g_assert(l == &inv_list || l == &look_list);
01298    }
01299 
01300    l->show_icon = new_setting;
01301 }
01302 
01303 void set_show_icon (const char *s)
01304 {
01305     if (s == NULL || *s == 0 || strncmp ("inventory", s, strlen(s)) == 0) {
01306         itemlist_set_show_icon(&inv_list, !inv_list.show_icon); /* toggle */
01307     } else if (strncmp ("look", s, strlen(s)) == 0) {
01308         itemlist_set_show_icon(&look_list, !look_list.show_icon); /* toggle */
01309     }
01310 }
01311 
01312 
01313 /* when containers are opened and shut */
01314 
01315 static void set_look_list_env_one(gpointer view_x, gpointer new_look_x) {
01316     inventory_viewer * view = (inventory_viewer *)view_x;
01317     item * new_look = (item *)new_look_x;
01318 
01319     view->move_dest = new_look;
01320 }
01321 
01322 /* Also called on disconnect. */
01323 void set_look_list_env(item * op) {
01324     if (look_list.env == op) return;
01325 
01326     look_list.env = op;
01327 
01328     inventory_viewer_set_container(look_viewer, op);
01329 
01330     g_list_foreach(inv_viewers, set_look_list_env_one, op);
01331 }
01332 
01333 
01334 void open_container (item *op) {
01335   set_look_list_env(op);
01336   sprintf (look_list.title, "%s:", op->d_name);
01337   gtk_widget_set_sensitive(closebutton, TRUE);
01338 
01339   update_list_labels (&look_list);
01340 }
01341 
01342 void close_container(item *op)
01343 {
01344   if (look_list.env != cpl.below) {
01345     if (use_config[CONFIG_APPLY_CONTAINER])
01346         client_send_apply (look_list.env->tag);
01347     set_look_list_env(cpl.below);
01348     strcpy (look_list.title, "You see:");
01349     gtk_widget_set_sensitive(closebutton, FALSE);
01350     update_list_labels (&look_list);
01351   }
01352 }
01353 
01354 /* This is basically the same as above, but is used for the callback
01355  * of the close button.  As such, it has to always send the apply.
01356  * However, since its a callback, its not like we can just easily
01357  * pass additional parameters.
01358  */
01359 static void close_container_callback(item *op)
01360 {
01361   if (look_list.env != cpl.below) {
01362     client_send_apply (look_list.env->tag);
01363     set_look_list_env(cpl.below);
01364     strcpy (look_list.title, "You see:");
01365     gtk_widget_set_sensitive(closebutton, FALSE);
01366 
01367     update_list_labels (&look_list);
01368   }
01369 }
01370 
01371 /* --- */
01372 
01373 /* Called by gx11::do_timeout(). */
01374 void inventory_tick(void) {
01375     update_lists_labels();
01376     itemview_tick();
01377 }