Crossfire Client, Branch
R11627
|
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 }