URI:
       ttreeview.c - vaccinewars - be a doctor and try to vaccinate the world
  HTML git clone git://src.adamsgaard.dk/vaccinewars
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       ttreeview.c (36602B)
       ---
            1 /************************************************************************
            2  * treeview.c     GtkTreeView (and friends) implementation for gtkport  *
            3  * Copyright (C)  1998-2021  Ben Webb                                   *
            4  *                Email: benwebb@users.sf.net                           *
            5  *                WWW: https://dopewars.sourceforge.io/                 *
            6  *                                                                      *
            7  * This program is free software; you can redistribute it and/or        *
            8  * modify it under the terms of the GNU General Public License          *
            9  * as published by the Free Software Foundation; either version 2       *
           10  * of the License, or (at your option) any later version.               *
           11  *                                                                      *
           12  * This program is distributed in the hope that it will be useful,      *
           13  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
           14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
           15  * GNU General Public License for more details.                         *
           16  *                                                                      *
           17  * You should have received a copy of the GNU General Public License    *
           18  * along with this program; if not, write to the Free Software          *
           19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,               *
           20  *                   MA  02111-1307, USA.                               *
           21  ************************************************************************/
           22 
           23 #ifdef HAVE_CONFIG_H
           24 #include <config.h>
           25 #endif
           26 
           27 #include "gtkport.h"
           28 
           29 #ifdef CYGWIN
           30 
           31 #include <winsock2.h>
           32 #include <windows.h>
           33 #include <commctrl.h>
           34 
           35 #include "unicodewrap.h"
           36 
           37 #define LISTITEMHPACK  3
           38 #define LISTHEADERPACK 6
           39 
           40 static const gchar *WC_GTKTREEVIEWHDR = "WC_GTKTREEVIEWHDR";
           41 
           42 static WNDPROC wpOrigListProc;
           43 
           44 static void gtk_tree_view_size_request(GtkWidget *widget,
           45                                        GtkRequisition *requisition);
           46 static void gtk_tree_view_set_size(GtkWidget *widget,
           47                                     GtkAllocation *allocation);
           48 static gboolean gtk_tree_view_wndproc(GtkWidget *widget, UINT msg,
           49                 WPARAM wParam, LPARAM lParam, gboolean *dodef);
           50 static void gtk_tree_view_realize(GtkWidget *widget);
           51 static void gtk_tree_view_destroy(GtkWidget *widget);
           52 static void gtk_tree_view_show(GtkWidget *widget);
           53 static void gtk_tree_view_hide(GtkWidget *widget);
           54 static void gtk_tree_view_draw_row(GtkTreeView *tv, LPDRAWITEMSTRUCT lpdis);
           55 static void gtk_tree_view_update_selection(GtkWidget *widget);
           56 static void gtk_tree_view_update_widths(GtkTreeView *tv, GtkTreeModel *model,
           57                                         GtkListStoreRow *row);
           58 static void gtk_tree_view_update_all_widths(GtkTreeView *tv);
           59 static void gtk_tree_view_do_auto_resize(GtkTreeView *tv);
           60 static void gtk_tree_view_set_column_width(GtkTreeView *tv, gint column,
           61                                            gint width);
           62 static void gtk_tree_view_set_column_width_full(GtkTreeView *tv, gint column,
           63                                                 gint width,
           64                                                 gboolean ResizeHeader);
           65 static void gtk_tree_view_click_column(GtkWidget *widget, gint column);
           66 
           67 static GtkSignalType GtkTreeViewSignals[] = {
           68   {"size_request", gtk_marshal_VOID__GPOIN, gtk_tree_view_size_request},
           69   {"set_size", gtk_marshal_VOID__GPOIN, gtk_tree_view_set_size},
           70   {"realize", gtk_marshal_VOID__VOID, gtk_tree_view_realize},
           71   {"destroy", gtk_marshal_VOID__VOID, gtk_tree_view_destroy},
           72   {"click-column", gtk_marshal_VOID__GINT, gtk_tree_view_click_column},
           73   {"changed", gtk_marshal_VOID__VOID, NULL},
           74   {"show", gtk_marshal_VOID__VOID, gtk_tree_view_show},
           75   {"hide", gtk_marshal_VOID__VOID, gtk_tree_view_hide},
           76   {"", NULL, NULL}
           77 };
           78 
           79 static GtkClass GtkTreeViewClass = {
           80   "tree_view", &GtkContainerClass, sizeof(GtkTreeView), GtkTreeViewSignals,
           81   gtk_tree_view_wndproc
           82 };
           83 
           84 static GtkClass GtkListStoreClass = {
           85   "list_store", &GtkObjectClass, sizeof(GtkListStore), NULL, NULL
           86 };
           87 
           88 static void SetTreeViewHeaderSize(GtkTreeView *clist)
           89 {
           90   RECT rc;
           91   HWND hWnd;
           92   int width;
           93 
           94   hWnd = GTK_WIDGET(clist)->hWnd;
           95   clist->scrollpos = GetScrollPos(hWnd, SB_HORZ);
           96 
           97   GetWindowRect(hWnd, &rc);
           98   width = (int)SendMessageW(hWnd, LB_GETHORIZONTALEXTENT, 0, 0);
           99   width = MAX(width, rc.right - rc.left) + 100;
          100 
          101   SetWindowPos(clist->header, HWND_TOP, -clist->scrollpos, 0,
          102                width, clist->header_size, SWP_NOZORDER);
          103 }
          104 
          105 static LRESULT APIENTRY ListWndProc(HWND hwnd, UINT msg, WPARAM wParam,
          106                                     LPARAM lParam)
          107 {
          108   LRESULT retval;
          109   GtkWidget *widget;
          110 
          111   widget = GTK_WIDGET(GetWindowLongPtr(hwnd, GWLP_USERDATA));
          112   retval = CallWindowProcW(wpOrigListProc, hwnd, msg, wParam, lParam);
          113 
          114   if (msg == WM_HSCROLL && widget) {
          115     GtkTreeView *clist = GTK_TREE_VIEW(widget);
          116     SetTreeViewHeaderSize(clist);
          117   }
          118 
          119   return retval;
          120 }
          121 
          122 gboolean gtk_tree_view_wndproc(GtkWidget *widget, UINT msg, WPARAM wParam,
          123                                LPARAM lParam, gboolean *dodef)
          124 {
          125   LPDRAWITEMSTRUCT lpdis;
          126   HD_NOTIFYA FAR *phdr;
          127   HD_NOTIFYW FAR *phdrw;
          128   NMHDR *nmhdr;
          129 
          130   switch(msg) {
          131   case WM_COMMAND:
          132     if (lParam && HIWORD(wParam) == LBN_SELCHANGE) {
          133       gtk_tree_view_update_selection(widget);
          134       return FALSE;
          135     }
          136     break;
          137   case WM_DRAWITEM:
          138     lpdis = (LPDRAWITEMSTRUCT)lParam;
          139     if (lpdis) {
          140       gtk_tree_view_draw_row(GTK_TREE_VIEW(widget), lpdis);
          141       *dodef = FALSE;
          142       return TRUE;
          143     }
          144     break;
          145   case WM_NOTIFY:
          146     nmhdr = (NMHDR *)lParam;
          147     if (nmhdr) {
          148       switch(nmhdr->code) {
          149       case HDN_ENDTRACKA:
          150         phdr = (HD_NOTIFYA FAR *)lParam;
          151         gtk_tree_view_set_column_width_full(GTK_TREE_VIEW(widget), phdr->iItem,
          152                                             phdr->pitem->cxy, FALSE);
          153         return FALSE;
          154       case HDN_ENDTRACKW:
          155         phdrw = (HD_NOTIFYW FAR *)lParam;
          156         gtk_tree_view_set_column_width_full(GTK_TREE_VIEW(widget), phdrw->iItem,
          157                                             phdrw->pitem->cxy, FALSE);
          158         return FALSE;
          159       case HDN_ITEMCLICKA:
          160         phdr = (HD_NOTIFYA FAR *)lParam;
          161         gtk_signal_emit(G_OBJECT(widget), "click-column", (gint)phdr->iItem);
          162         return FALSE;
          163       case HDN_ITEMCLICKW:
          164         phdrw = (HD_NOTIFYW FAR *)lParam;
          165         gtk_signal_emit(G_OBJECT(widget), "click-column", (gint)phdrw->iItem);
          166         return FALSE;
          167       default:
          168         break;
          169       }
          170     }
          171     break;
          172   }
          173 
          174   return FALSE;
          175 }
          176 
          177 static void gtk_tree_view_set_extent(GtkTreeView *tv)
          178 {
          179   HWND hWnd;
          180 
          181   hWnd = GTK_WIDGET(tv)->hWnd;
          182   if (hWnd) {
          183     GSList *colpt;
          184     int width = 0;
          185 
          186     for (colpt = tv->columns; colpt; colpt = g_slist_next(colpt)) {
          187       GtkTreeViewColumn *col = colpt->data;
          188       width += col->width;
          189     }
          190     SendMessageW(hWnd, LB_SETHORIZONTALEXTENT, (WPARAM)width, 0);
          191     SetTreeViewHeaderSize(tv);
          192   }
          193 }
          194 
          195 void gtk_tree_view_set_size(GtkWidget *widget, GtkAllocation *allocation)
          196 {
          197   GtkTreeView *clist = GTK_TREE_VIEW(widget);
          198 
          199   gtk_container_set_size(widget, allocation);
          200   if (clist->header) {
          201     POINT pt;
          202     pt.x = allocation->x;
          203     pt.y = allocation->y;
          204     MapWidgetOrigin(widget, &pt);
          205     SetWindowPos(clist->scrollwin, HWND_TOP, pt.x, pt.y,
          206                  allocation->width, clist->header_size, SWP_NOZORDER);
          207     allocation->y += clist->header_size - 1;
          208     allocation->height -= clist->header_size - 1;
          209   }
          210   gtk_tree_view_set_extent(clist);
          211 }
          212 
          213 GtkWidget *gtk_tree_view_new(void)
          214 {
          215   GtkTreeView *view;
          216 
          217   view = GTK_TREE_VIEW(GtkNewObject(&GtkTreeViewClass));
          218   view->model = NULL;
          219   view->scrollpos = 0;
          220   view->columns = NULL;
          221   view->headers_clickable = TRUE;
          222   view->mode = GTK_SELECTION_SINGLE;
          223   view->selection = NULL;
          224   return GTK_WIDGET(view);
          225 }
          226 
          227 GtkTreeSelection *gtk_tree_view_get_selection(GtkTreeView *tree_view)
          228 {
          229   /* The selection *is* the tree view */
          230   return tree_view;
          231 }
          232 
          233 void gtk_tree_view_size_request(GtkWidget *widget, GtkRequisition *requisition)
          234 {
          235   SIZE size;
          236 
          237   if (GetTextSize(widget->hWnd, "Sample text", &size, defFont)) {
          238     requisition->width = size.cx;
          239     requisition->height = size.cy * 6 + 12;
          240   }
          241 }
          242 
          243 void gtk_tree_view_realize(GtkWidget *widget)
          244 {
          245   HWND Parent, header, scrollwin;
          246   HD_LAYOUT hdl;
          247   HD_ITEM hdi;
          248   RECT rcParent;
          249   WINDOWPOS wp;
          250   GtkTreeView *tv = GTK_TREE_VIEW(widget);
          251   GSList *colpt;
          252   gint i;
          253 
          254   gtk_container_realize(widget);
          255   Parent = gtk_get_parent_hwnd(widget);
          256   GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
          257   rcParent.left = rcParent.top = 0;
          258   rcParent.right = rcParent.bottom = 800;
          259   scrollwin = myCreateWindow(WC_GTKTREEVIEWHDR, NULL, WS_CHILD | WS_BORDER,
          260                              0, 0, 0, 0, Parent, NULL, hInst, NULL);
          261   SetWindowLongPtr(scrollwin, GWLP_USERDATA, (LONG_PTR)widget);
          262   header = myCreateWindowEx(0, WC_HEADER, NULL,
          263                             WS_CHILD | HDS_HORZ | WS_VISIBLE
          264                             | (tv->headers_clickable ? HDS_BUTTONS : 0),
          265                             0, 0, 0, 0, scrollwin, NULL, hInst, NULL);
          266   SetWindowLongPtr(header, GWLP_USERDATA, (LONG_PTR)widget);
          267   tv->header = header;
          268   tv->scrollwin = scrollwin;
          269   gtk_set_default_font(header);
          270   hdl.prc = &rcParent;
          271   hdl.pwpos = &wp;
          272   SendMessageW(header, HDM_LAYOUT, 0, (LPARAM)&hdl);
          273   tv->header_size = wp.cy;
          274   widget->hWnd = myCreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "",
          275                                   WS_CHILD | WS_TABSTOP | WS_VSCROLL
          276                                   | WS_HSCROLL | LBS_OWNERDRAWFIXED
          277                                   | LBS_NOTIFY, 0, 0, 0, 0, Parent, NULL,
          278                                   hInst, NULL);
          279   /* Subclass the window */
          280   wpOrigListProc = (WNDPROC)SetWindowLongPtrW(widget->hWnd, GWLP_WNDPROC,
          281                                               (LONG_PTR)ListWndProc);
          282   gtk_set_default_font(widget->hWnd);
          283 
          284   if (tv->model) {
          285     for (i = 0; i < tv->model->rows->len; ++i) {
          286       SendMessageW(widget->hWnd, LB_ADDSTRING, 0, 1);
          287     }
          288   }
          289   gtk_tree_view_update_all_widths(tv);
          290 
          291   for (colpt = tv->columns, i = 0; colpt; colpt = g_slist_next(colpt), ++i) {
          292     GtkTreeViewColumn *col = colpt->data;
          293     if (col->auto_resize) {
          294       col->width = col->optimal_width;
          295     }
          296     hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
          297     hdi.pszText = col->title;
          298     if (hdi.pszText) {
          299       if (!g_slist_next(colpt))
          300         hdi.cxy = 9000;
          301       else
          302         hdi.cxy = col->width;
          303       hdi.cchTextMax = strlen(hdi.pszText);
          304       hdi.fmt = HDF_LEFT | HDF_STRING;
          305       myHeader_InsertItem(header, i + 1, &hdi);
          306     }
          307   }
          308 }
          309 
          310 static void gtk_list_store_row_free(GtkListStoreRow *row, GtkListStore *store)
          311 {
          312   int i;
          313   for (i = 0; i < store->ncols; ++i) {
          314     if (store->coltype[i] == G_TYPE_STRING) {
          315       g_free(row->data[i]);
          316     }
          317   }
          318 }
          319 
          320 void gtk_list_store_clear(GtkListStore *list_store)
          321 {
          322   guint i;
          323   for (i = 0; i < list_store->rows->len; ++i) {
          324     GtkListStoreRow *row = &g_array_index(list_store->rows, GtkListStoreRow, i);
          325     gtk_list_store_row_free(row, list_store);
          326   }
          327   g_array_set_size(list_store->rows, 0);
          328   list_store->need_sort = FALSE;  /* an empty store is sorted */
          329 
          330   if (list_store->view) {
          331     HWND hWnd;
          332     gtk_tree_view_update_all_widths(list_store->view);
          333     hWnd = GTK_WIDGET(list_store->view)->hWnd;
          334     if (hWnd) {
          335       SendMessageW(hWnd, LB_RESETCONTENT, 0, 0);
          336     }
          337   }
          338 }
          339 
          340 void gtk_list_store_insert(GtkListStore *list_store, GtkTreeIter *iter,
          341                            gint position)
          342 {
          343   GtkListStoreRow row;
          344   /* Add a new empty row to the store and return a pointer to it */
          345   row.data = g_new0(gpointer, list_store->ncols);
          346   if (position < 0) {
          347     g_array_append_val(list_store->rows, row);
          348     *iter = list_store->rows->len - 1;
          349   } else {
          350     g_array_insert_val(list_store->rows, position, row);
          351     *iter = position;
          352   }
          353 }
          354 
          355 void gtk_list_store_append(GtkListStore *list_store, GtkTreeIter *iter)
          356 {
          357   gtk_list_store_insert(list_store, iter, -1);
          358 }
          359 
          360 void gtk_list_store_set(GtkListStore *list_store, GtkTreeIter *iter, ...)
          361 {
          362   va_list ap;
          363   int colind;
          364   gboolean new_row = TRUE;
          365   GtkListStoreRow *row = &g_array_index(list_store->rows, GtkListStoreRow,
          366                                         *iter);
          367   list_store->need_sort = TRUE;
          368 
          369   va_start(ap, iter);
          370   while ((colind = va_arg(ap, int)) >= 0) {
          371     switch(list_store->coltype[colind]) {
          372     case G_TYPE_STRING:
          373       g_free(row->data[colind]);  /* Free any existing string */
          374       if (row->data[colind]) {
          375         new_row = FALSE;
          376       }
          377       row->data[colind] = g_strdup(va_arg(ap, const char*));
          378       break;
          379     case G_TYPE_UINT:
          380       row->data[colind] = GUINT_TO_POINTER(va_arg(ap, unsigned));
          381       break;
          382     case G_TYPE_INT:
          383       row->data[colind] = GINT_TO_POINTER(va_arg(ap, int));
          384       break;
          385     case G_TYPE_POINTER:
          386       row->data[colind] = va_arg(ap, gpointer);
          387       break;
          388     }
          389   }
          390   va_end(ap);
          391 
          392   if (list_store->view) {
          393     GtkWidget *widget = GTK_WIDGET(list_store->view);
          394 
          395     gtk_tree_view_update_widths(list_store->view, list_store, row);
          396     gtk_tree_view_do_auto_resize(list_store->view);
          397 
          398     if (GTK_WIDGET_REALIZED(widget)) {
          399       HWND hWnd = widget->hWnd;
          400       if (new_row) {
          401         SendMessageW(hWnd, LB_INSERTSTRING, (WPARAM)*iter, 1);
          402       } else {
          403         InvalidateRect(hWnd, NULL, FALSE);
          404       }
          405     }
          406   }
          407 }
          408 
          409 void gtk_list_store_swap(GtkListStore *store, GtkTreeIter *a, GtkTreeIter *b)
          410 {
          411   GtkTreeIter tmp;
          412   GtkListStoreRow rowa = g_array_index(store->rows, GtkListStoreRow, *a);
          413   GtkListStoreRow rowb = g_array_index(store->rows, GtkListStoreRow, *b);
          414 
          415   g_array_index(store->rows, GtkListStoreRow, *a) = rowb;
          416   g_array_index(store->rows, GtkListStoreRow, *b) = rowa;
          417   store->need_sort = TRUE;
          418 
          419   /* Swap the iterators too since in our implementation they are just row
          420      indices */
          421   tmp = *a;
          422   *a = *b;
          423   *b = tmp;
          424 }
          425 
          426 void gtk_tree_model_get(GtkTreeModel *tree_model, GtkTreeIter *iter, ...)
          427 {
          428   va_list ap;
          429   char **strpt;
          430   unsigned *uintpt;
          431   int *intpt;
          432   gpointer *ptpt;
          433   int colind;
          434   GtkListStoreRow *row = &g_array_index(tree_model->rows, GtkListStoreRow,
          435                                         *iter);
          436 
          437   va_start(ap, iter);
          438   while ((colind = va_arg(ap, int)) >= 0) {
          439     switch(tree_model->coltype[colind]) {
          440     case G_TYPE_STRING:
          441       strpt = va_arg(ap, char **);
          442       *strpt = g_strdup(row->data[colind]);
          443       break;
          444     case G_TYPE_UINT:
          445       uintpt = va_arg(ap, unsigned *);
          446       *uintpt = GPOINTER_TO_UINT(row->data[colind]);
          447       break;
          448     case G_TYPE_INT:
          449       intpt = va_arg(ap, int *);
          450       *intpt = GPOINTER_TO_INT(row->data[colind]);
          451       break;
          452     case G_TYPE_POINTER:
          453       ptpt = va_arg(ap, gpointer *);
          454       *ptpt = row->data[colind];
          455       break;
          456     }
          457   }
          458   va_end(ap);
          459 }
          460 
          461 gboolean gtk_tree_model_iter_nth_child(GtkTreeModel *tree_model,
          462                                        GtkTreeIter *iter,
          463                                        GtkTreeIter *parent, gint n)
          464 {
          465   /* We only work with one level (lists) for now */
          466   g_assert(parent == NULL);
          467   *iter = n;
          468   return TRUE;
          469 }
          470 
          471 gint gtk_tree_model_iter_n_children(GtkTreeModel *tree_model,
          472                                     GtkTreeIter *iter)
          473 {
          474   /* We only work with one level (lists) for now */
          475   if (iter) {
          476     return 1;
          477   } else {
          478     return tree_model->rows->len;
          479   }
          480 }
          481 
          482 
          483 static void gtk_tree_view_column_free(gpointer data)
          484 {
          485   GtkTreeViewColumn *col = data;
          486   g_free(col->title);
          487   g_free(col);
          488 }
          489 
          490 void gtk_tree_model_free(GtkTreeModel *model)
          491 {
          492   gtk_list_store_clear(model);  /* Remove all rows */
          493   g_array_free(model->rows, TRUE);
          494   g_array_free(model->sort_func, TRUE);
          495   g_free(model->coltype);
          496   g_free(model);
          497 }
          498 
          499 void gtk_tree_view_destroy(GtkWidget *widget)
          500 {
          501   GtkTreeView *view = GTK_TREE_VIEW(widget);
          502   g_slist_free_full(view->columns, gtk_tree_view_column_free);
          503   view->columns = NULL;
          504   if (view->model) {
          505       gtk_tree_model_free(view->model);
          506   }
          507   view->model = NULL;
          508 }
          509 
          510 void gtk_tree_view_click_column(GtkWidget *widget, gint column)
          511 {
          512   GtkTreeView *view = GTK_TREE_VIEW(widget);
          513   GtkTreeViewColumn *col = g_slist_nth_data(view->columns, column);
          514   GtkListStore *model = view->model;
          515   if (!model || !view->headers_clickable) return;
          516 
          517   if (col->sort_column_id == model->sort_column_id) {
          518     /* toggle order */
          519     if (model->sort_order == GTK_SORT_ASCENDING) {
          520       model->sort_order = GTK_SORT_DESCENDING;
          521     } else {
          522       model->sort_order = GTK_SORT_ASCENDING;
          523     }
          524   } else {
          525     model->sort_column_id = col->sort_column_id;
          526     model->sort_order = GTK_SORT_ASCENDING;
          527   }
          528   model->need_sort = TRUE;
          529   gtk_tree_view_sort(view);
          530 }
          531 
          532 void gtk_tree_view_show(GtkWidget *widget)
          533 {
          534   if (GTK_WIDGET_REALIZED(widget)) {
          535     ShowWindow(GTK_TREE_VIEW(widget)->scrollwin, SW_SHOWNORMAL);
          536   }
          537 }
          538 
          539 void gtk_tree_view_hide(GtkWidget *widget)
          540 {
          541   if (GTK_WIDGET_REALIZED(widget)) {
          542     ShowWindow(GTK_TREE_VIEW(widget)->scrollwin, SW_HIDE);
          543   }
          544 }
          545 
          546 /* Draw an individual cell (row+column) */
          547 static void draw_cell_text(GtkTreeViewColumn *col, GtkTreeModel *model,
          548                            LPDRAWITEMSTRUCT lpdis, GtkListStoreRow *row,
          549                            RECT *rcCol)
          550 {
          551   UINT align;
          552   char *val;
          553   int modcol = col->model_column;
          554   /* Convert float 0.0, 0.5 or 1.0 into int, allow for some rounding error */
          555   switch((int)(col->xalign * 10. + 0.1)) {
          556   case 10:
          557     align = DT_RIGHT;
          558     break;
          559   case 5:
          560     align = DT_CENTER;
          561     break;
          562   default:
          563     align = DT_LEFT;
          564     break;
          565   }
          566   align |= DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS;
          567 
          568   switch(model->coltype[modcol]) {
          569   case G_TYPE_STRING:
          570     if (row->data[modcol]) {
          571       myDrawText(lpdis->hDC, row->data[modcol], -1, rcCol, align);
          572     }
          573     break;
          574   case G_TYPE_UINT:
          575     val = g_strdup_printf("%u", GPOINTER_TO_UINT(row->data[modcol]));
          576     myDrawText(lpdis->hDC, val, -1, rcCol, align);
          577     g_free(val);
          578     break;
          579   case G_TYPE_INT:
          580     val = g_strdup_printf("%d", GPOINTER_TO_INT(row->data[modcol]));
          581     myDrawText(lpdis->hDC, val, -1, rcCol, align);
          582     g_free(val);
          583     break;
          584   }
          585 }
          586 
          587 void gtk_tree_view_draw_row(GtkTreeView *tv, LPDRAWITEMSTRUCT lpdis)
          588 {
          589   HBRUSH bkgrnd;
          590   COLORREF textcol, oldtextcol;
          591   RECT rcCol;
          592   int oldbkmode;
          593   guint nrows;
          594   gint CurrentX, right;
          595   GtkListStoreRow *row;
          596 
          597   if (lpdis->itemState & ODS_SELECTED) {
          598     bkgrnd = (HBRUSH)(1 + COLOR_HIGHLIGHT);
          599     textcol = (COLORREF)GetSysColor(COLOR_HIGHLIGHTTEXT);
          600   } else {
          601     bkgrnd = (HBRUSH)(1 + COLOR_WINDOW);
          602     textcol = (COLORREF)GetSysColor(COLOR_WINDOWTEXT);
          603   }
          604   oldtextcol = SetTextColor(lpdis->hDC, textcol);
          605   oldbkmode = SetBkMode(lpdis->hDC, TRANSPARENT);
          606   FillRect(lpdis->hDC, &lpdis->rcItem, bkgrnd);
          607 
          608   nrows = tv->model ? tv->model->rows->len : 0;
          609   if (lpdis->itemID >= 0 && lpdis->itemID < nrows) {
          610     int width;
          611     GSList *colpt;
          612     row = &g_array_index(tv->model->rows, GtkListStoreRow, lpdis->itemID);
          613     width = CurrentX = 0;
          614     for (colpt = tv->columns; colpt; colpt = g_slist_next(colpt)) {
          615       GtkTreeViewColumn *col = colpt->data;
          616       width += col->width;
          617     }
          618     right = MAX(lpdis->rcItem.right, width);
          619     rcCol.top = lpdis->rcItem.top;
          620     rcCol.bottom = lpdis->rcItem.bottom;
          621     if (row->data)
          622       for (colpt = tv->columns; colpt; colpt = g_slist_next(colpt)) {
          623         GtkTreeViewColumn *col = colpt->data;
          624         rcCol.left = CurrentX + LISTITEMHPACK;
          625         CurrentX += col->width;
          626         rcCol.right = CurrentX - LISTITEMHPACK;
          627         if (rcCol.left > right)
          628           rcCol.left = right;
          629         if (rcCol.right > right - LISTITEMHPACK)
          630           rcCol.right = right - LISTITEMHPACK;
          631         if (!g_slist_next(colpt))
          632           rcCol.right = right - LISTITEMHPACK;
          633         draw_cell_text(col, tv->model, lpdis, row, &rcCol);
          634       }
          635   }
          636 
          637   SetTextColor(lpdis->hDC, oldtextcol);
          638   SetBkMode(lpdis->hDC, oldbkmode);
          639   if (lpdis->itemState & ODS_FOCUS) {
          640     DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
          641   }
          642 }
          643 
          644 void gtk_tree_view_do_auto_resize(GtkTreeView *tv)
          645 {
          646   GSList *colpt;
          647   gint i;
          648 
          649   for (colpt = tv->columns, i = 0; colpt; colpt = g_slist_next(colpt), i++) {
          650     GtkTreeViewColumn *col = colpt->data;
          651     if (col->auto_resize) {
          652       gtk_tree_view_set_column_width(tv, i, col->optimal_width);
          653     }
          654   }
          655 }
          656 
          657 gint gtk_tree_view_optimal_column_width(GtkTreeView *tv, gint column)
          658 {
          659   GtkTreeViewColumn *col = g_slist_nth_data(tv->columns, column);
          660   return col->optimal_width;
          661 }
          662 
          663 void gtk_tree_view_update_all_widths(GtkTreeView *tv)
          664 {
          665   SIZE size;
          666   HWND header;
          667   gint i;
          668 
          669   header = tv->header;
          670   if (header) {
          671     GSList *colpt;
          672     for (colpt = tv->columns, i = 0; colpt; colpt = g_slist_next(colpt), i++) {
          673       GtkTreeViewColumn *col = colpt->data;
          674       if (GetTextSize(header, col->title, &size, defFont)) {
          675         int new_width = size.cx + 4 + 2 * LISTHEADERPACK;
          676         col->width = MAX(col->width, new_width);
          677         col->optimal_width = MAX(col->optimal_width, new_width);
          678       }
          679     }
          680   }
          681 
          682   if (tv->model) {
          683     for (i = 0; i < tv->model->rows->len; ++i) {
          684       GtkListStoreRow *row = &g_array_index(tv->model->rows,
          685                                             GtkListStoreRow, i);
          686       gtk_tree_view_update_widths(tv, tv->model, row);
          687     }
          688   }
          689 
          690   gtk_tree_view_set_extent(tv);
          691 }
          692 
          693 void gtk_tree_view_update_widths(GtkTreeView *tv, GtkTreeModel *model,
          694                                  GtkListStoreRow *row)
          695 {
          696   SIZE size;
          697   GSList *colpt;
          698   HWND hWnd;
          699 
          700   hWnd = GTK_WIDGET(tv)->hWnd;
          701   if (!hWnd)
          702     return;
          703   for (colpt = tv->columns; colpt; colpt = g_slist_next(colpt)) {
          704     GtkTreeViewColumn *col = colpt->data;
          705     int modcol = col->model_column;
          706     char *text;
          707     switch (model->coltype[modcol]) {
          708     case G_TYPE_STRING:
          709       text = row->data[modcol];
          710       break;
          711     case G_TYPE_UINT:
          712     case G_TYPE_INT:
          713       text = "9999"; /* hack */
          714       break;
          715     default:
          716       text = NULL;
          717     }
          718     if (text && GetTextSize(hWnd, text, &size, defFont)) {
          719       int new_width = size.cx + 4 + 2 * LISTITEMHPACK;
          720       col->optimal_width = MAX(col->optimal_width, new_width);
          721     }
          722   }
          723 }
          724 
          725 gboolean gtk_list_store_remove(GtkListStore *list_store, GtkTreeIter *iter)
          726 {
          727   gint rowind = *iter;
          728   if (rowind >= 0 && rowind < list_store->rows->len) {
          729     GtkListStoreRow *row = &g_array_index(list_store->rows,
          730                                           GtkListStoreRow, rowind);
          731     gtk_list_store_row_free(row, list_store);
          732     g_array_remove_index(list_store->rows, rowind);
          733 
          734     if (list_store->view && GTK_WIDGET_REALIZED(GTK_WIDGET(list_store->view))) {
          735       HWND hWnd = GTK_WIDGET(list_store->view)->hWnd;
          736 
          737       SendMessageW(hWnd, LB_DELETESTRING, (WPARAM)rowind, 0);
          738     }
          739     return TRUE;
          740   } else {
          741     return FALSE;
          742   }
          743 }
          744 
          745 GtkWidget *gtk_scrolled_tree_view_new(GtkWidget **pack_widg)
          746 {
          747   GtkWidget *widget;
          748 
          749   widget = gtk_tree_view_new();
          750   *pack_widg = widget;
          751   return widget;
          752 }
          753 
          754 void gtk_tree_view_set_column_width(GtkTreeView *tv, gint column, gint width)
          755 {
          756   gtk_tree_view_set_column_width_full(tv, column, width, TRUE);
          757 }
          758 
          759 void gtk_tree_view_set_column_width_full(GtkTreeView *tv, gint column,
          760                                          gint width, gboolean ResizeHeader)
          761 {
          762   int ncols;
          763   GtkTreeViewColumn *col;
          764   HWND hWnd, header;
          765   HD_ITEM hdi;
          766 
          767   ncols = g_slist_length(tv->columns);
          768   if (column < 0 || column >= ncols)
          769     return;
          770   col = g_slist_nth_data(tv->columns, column);
          771 
          772   col->width = width;
          773   if (GTK_WIDGET_REALIZED(GTK_WIDGET(tv))) {
          774     header = tv->header;
          775     if (ResizeHeader && header) {
          776       hdi.mask = HDI_WIDTH;
          777       if (column == ncols - 1)
          778         width = 9000;
          779       hdi.cxy = width;
          780       if (SendMessageW(header, HDM_GETITEM, (WPARAM)column, (LPARAM)&hdi)
          781           && hdi.cxy != width) {
          782         hdi.mask = HDI_WIDTH;
          783         hdi.cxy = width;
          784         SendMessageW(header, HDM_SETITEM, (WPARAM)column, (LPARAM)&hdi);
          785       }
          786     }
          787     gtk_tree_view_set_extent(tv);
          788     hWnd = GTK_WIDGET(tv)->hWnd;
          789     if (hWnd)
          790       InvalidateRect(hWnd, NULL, FALSE);
          791   }
          792 }
          793 
          794 void gtk_tree_selection_set_mode(GtkTreeSelection *selection,
          795                                  GtkSelectionMode type)
          796 {
          797   selection->mode = type;
          798 }
          799 
          800 void gtk_tree_selection_select_path(GtkTreeSelection *selection,
          801                                     GtkTreePath *path)
          802 {
          803   HWND hWnd;
          804   guint row = *path;
          805 
          806   hWnd = GTK_WIDGET(selection)->hWnd;
          807   if (hWnd) {
          808     if (selection->mode == GTK_SELECTION_SINGLE) {
          809       SendMessageW(hWnd, LB_SETCURSEL, (WPARAM)row, 0);
          810     } else {
          811       SendMessageW(hWnd, LB_SETSEL, (WPARAM)TRUE, (LPARAM)row);
          812     }
          813     gtk_tree_view_update_selection(GTK_WIDGET(selection));
          814   }
          815 }
          816 
          817 void gtk_tree_selection_unselect_all(GtkTreeSelection *selection)
          818 {
          819   GList *sel;
          820   for (sel = selection->selection; sel; sel = g_list_next(sel)) {
          821     guint row = GPOINTER_TO_UINT(sel->data);
          822     gtk_tree_selection_unselect_path(selection, &row);
          823   }
          824 }
          825 
          826 GList *gtk_tree_selection_get_selected_rows(GtkTreeSelection *selection,
          827                                             GtkTreeModel **model)
          828 {
          829   GList *sel, *pathsel = NULL;
          830   for (sel = selection->selection; sel; sel = g_list_next(sel)) {
          831     guint row = GPOINTER_TO_UINT(sel->data);
          832     GtkTreePath *path = g_new(GtkTreePath, 1);
          833     *path = row;
          834     pathsel = g_list_append(pathsel, path);
          835   }
          836   if (model) {
          837     *model = selection->model;
          838   }
          839   return pathsel;
          840 }
          841 
          842 gint *gtk_tree_path_get_indices_with_depth(GtkTreePath *path, gint *depth)
          843 {
          844   /* Only one level; path *is* the row index */
          845   *depth = 1;
          846   return (gint *)path;
          847 }
          848 
          849 void gtk_tree_selection_unselect_path(GtkTreeSelection *selection,
          850                                       GtkTreePath *path)
          851 {
          852   HWND hWnd;
          853   guint row = *path;
          854 
          855   hWnd = GTK_WIDGET(selection)->hWnd;
          856   if (hWnd) {
          857     if (selection->mode == GTK_SELECTION_SINGLE) {
          858       SendMessageW(hWnd, LB_SETCURSEL, (WPARAM)(-1), 0);
          859     } else {
          860       SendMessageW(hWnd, LB_SETSEL, (WPARAM)FALSE, (LPARAM)row);
          861     }
          862     gtk_tree_view_update_selection(GTK_WIDGET(selection));
          863   }
          864 }
          865 
          866 gint gtk_tree_selection_count_selected_rows(GtkTreeSelection *selection)
          867 {
          868   return g_list_length(selection->selection);
          869 }
          870 
          871 gboolean gtk_tree_selection_get_selected(GtkTreeSelection *selection,
          872                                          GtkTreeModel **model,
          873                                          GtkTreeIter *iter)
          874 {
          875   if (model) {
          876     *model = selection->model;
          877   }
          878 
          879   /* Just return the first selected row */
          880   if (selection->selection) {
          881     if (iter) {
          882       int row = GPOINTER_TO_INT(g_list_nth_data(selection->selection, 0));
          883       *iter = row;
          884     }
          885     return TRUE;
          886   } else {
          887     return FALSE;
          888   }
          889 }
          890 
          891 void gtk_tree_selection_selected_foreach(GtkTreeSelection *selection,
          892                                          GtkTreeSelectionForeachFunc func,
          893                                          gpointer data)
          894 {
          895   GList *sel;
          896   for (sel = selection->selection; sel; sel = g_list_next(sel)) {
          897     guint row = GPOINTER_TO_UINT(sel->data);
          898     func(selection->model, &row, &row, data);
          899   }
          900 }
          901 
          902 void gtk_tree_view_update_selection(GtkWidget *widget)
          903 {
          904   GtkTreeView *tv = GTK_TREE_VIEW(widget);
          905   gint i;
          906 
          907   g_list_free(tv->selection);
          908   tv->selection = NULL;
          909   if (widget->hWnd) {
          910     if (tv->model) for (i = 0; i < tv->model->rows->len; i++) {
          911       if (SendMessageW(widget->hWnd, LB_GETSEL, (WPARAM)i, 0) > 0) {
          912         tv->selection = g_list_append(tv->selection, GINT_TO_POINTER(i));
          913       }
          914     }
          915 
          916     gtk_signal_emit(G_OBJECT(widget), "changed");
          917   }
          918 }
          919 
          920 static LRESULT CALLBACK TreeViewHdrWndProc(HWND hwnd, UINT msg, WPARAM wParam,
          921                                            LPARAM lParam)
          922 {
          923   GtkWidget *widget;
          924   gboolean retval = FALSE, dodef = TRUE;
          925 
          926   widget = GTK_WIDGET(GetWindowLongPtr(hwnd, GWLP_USERDATA));
          927 
          928   if (widget) {
          929     retval = gtk_tree_view_wndproc(widget, msg, wParam, lParam, &dodef);
          930   }
          931 
          932   if (dodef) {
          933     return DefWindowProcW(hwnd, msg, wParam, lParam);
          934   } else {
          935     return retval;
          936   }
          937 }
          938 
          939 void InitTreeViewClass(HINSTANCE hInstance)
          940 {
          941   WNDCLASS wc;
          942 
          943   wc.style = 0;
          944   wc.lpfnWndProc = TreeViewHdrWndProc;
          945   wc.cbClsExtra = 0;
          946   wc.cbWndExtra = 0;
          947   wc.hInstance = hInstance;
          948   wc.hIcon = NULL;
          949   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
          950   wc.hbrBackground = NULL;
          951   wc.lpszMenuName = NULL;
          952   wc.lpszClassName = WC_GTKTREEVIEWHDR;
          953   myRegisterClass(&wc);
          954 }
          955 
          956 /* Make a new GtkListStore and fill in the column types */
          957 GtkListStore *gtk_list_store_new(gint n_columns, ...)
          958 {
          959   GtkListStore *store;
          960   int i;
          961 
          962   va_list ap;
          963   va_start(ap, n_columns);
          964 
          965   store = GTK_LIST_STORE(GtkNewObject(&GtkListStoreClass));
          966   store->view = NULL;
          967   store->ncols = n_columns;
          968   store->coltype = g_new(int, n_columns);
          969   store->rows = g_array_new(FALSE, FALSE, sizeof(GtkListStoreRow));
          970   store->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
          971   store->sort_func = g_array_new(FALSE, TRUE, sizeof(gpointer));
          972   store->need_sort = FALSE;
          973   for (i = 0; i < n_columns; ++i) {
          974     store->coltype[i] = va_arg(ap, int);
          975   }
          976   va_end(ap);
          977   return store;
          978 }
          979 
          980 void gtk_tree_sortable_set_sort_func(GtkTreeSortable *sortable,
          981                                      gint sort_column_id,
          982                                      GtkTreeIterCompareFunc sort_func,
          983                                      gpointer user_data,
          984                                      GDestroyNotify destroy)
          985 {
          986   /* We don't currently support user_data */
          987   if (sort_column_id >= sortable->sort_func->len) {
          988     g_array_set_size(sortable->sort_func, sort_column_id+1);
          989   }
          990   g_array_index(sortable->sort_func, gpointer, sort_column_id) = sort_func;
          991 }
          992 
          993 void gtk_tree_sortable_set_sort_column_id(GtkTreeSortable *sortable,
          994                                           gint sort_column_id,
          995                                           GtkSortType order)
          996 {
          997   if (sortable->sort_column_id != sort_column_id
          998       || sortable->sort_order != order) {
          999     sortable->sort_column_id = sort_column_id;
         1000     sortable->sort_order = order;
         1001     sortable->need_sort = TRUE;
         1002   }
         1003 }
         1004 
         1005 /* We don't support customizing renderers right now */
         1006 GtkCellRenderer *gtk_cell_renderer_text_new(void)
         1007 {
         1008   return NULL;
         1009 }
         1010 
         1011 static GtkTreeViewColumn *new_column_internal(const char *title, va_list args)
         1012 {
         1013   GtkTreeViewColumn *col;
         1014   const char *name;
         1015 
         1016   col = g_new0(GtkTreeViewColumn, 1);
         1017   col->title = g_strdup(title);
         1018   col->resizeable = FALSE;
         1019   col->expand = FALSE;
         1020   col->auto_resize = TRUE;
         1021   col->sort_column_id = -1;
         1022   col->model_column = -1;
         1023   col->xalign = 0.0;   /* left align by default */
         1024 
         1025   /* Currently we only support the "text" attribute to point to the
         1026      ListStore column */
         1027   while ((name = va_arg(args, const char *)) != NULL) {
         1028     if (strcmp(name, "text") == 0) {
         1029       col->model_column = va_arg(args, int);
         1030     }
         1031   }
         1032   return col;
         1033 }
         1034 
         1035 GtkTreeViewColumn *gtk_tree_view_column_new_with_attributes
         1036                    (const gchar *title, GtkCellRenderer *cell, ...)
         1037 {
         1038   GtkTreeViewColumn *col;
         1039   va_list args;
         1040 
         1041   va_start(args, cell);
         1042   col = new_column_internal(title, args);
         1043   va_end(args);
         1044   return col;
         1045 }
         1046 
         1047 gint gtk_tree_view_insert_column_with_attributes
         1048             (GtkTreeView *tree_view, gint position, const gchar *title,
         1049              GtkCellRenderer *cell, ...)
         1050 {
         1051   GtkTreeViewColumn *col;
         1052   va_list args;
         1053 
         1054   va_start(args, cell);
         1055   col = new_column_internal(title, args);
         1056   va_end(args);
         1057   return gtk_tree_view_insert_column(tree_view, col, position);
         1058 }
         1059 
         1060 void gtk_tree_view_scroll_to_cell(GtkTreeView *tree_view,
         1061                                   GtkTreePath *path,
         1062                                   GtkTreeViewColumn *column,
         1063                                   gboolean use_align, gfloat row_align,
         1064                                   gfloat col_align)
         1065 {
         1066   /* not implemented */
         1067 }
         1068 
         1069 void gtk_tree_view_column_set_resizable(GtkTreeViewColumn *tree_column,
         1070                                         gboolean resizable)
         1071 {
         1072   tree_column->resizeable = resizable;
         1073 }
         1074 
         1075 void gtk_tree_view_column_set_expand(GtkTreeViewColumn *tree_column,
         1076                                      gboolean expand)
         1077 {
         1078   tree_column->expand = expand;
         1079 }
         1080 
         1081 void gtk_tree_view_column_set_sort_column_id(GtkTreeViewColumn *tree_column,
         1082                                              gint sort_column_id)
         1083 {
         1084   tree_column->sort_column_id = sort_column_id;
         1085 }
         1086 
         1087 void gtk_tree_view_column_set_alignment(GtkTreeViewColumn *tree_column,
         1088                                         gfloat xalign)
         1089 {
         1090   tree_column->xalign = xalign;
         1091 }
         1092 
         1093 gint gtk_tree_view_insert_column(GtkTreeView *tree_view,
         1094                                  GtkTreeViewColumn *column,
         1095                                  gint position)
         1096 {
         1097   tree_view->columns = g_slist_insert(tree_view->columns, column, position);
         1098   return g_slist_length(tree_view->columns);
         1099 }
         1100 
         1101 GtkTreeViewColumn *gtk_tree_view_get_column(GtkTreeView *tree_view, gint n)
         1102 {
         1103   return g_slist_nth_data(tree_view->columns, n);
         1104 }
         1105 
         1106 void gtk_tree_view_set_model(GtkTreeView *tree_view, GtkTreeModel *model)
         1107 {
         1108   /* We only support a single model per view, so ignore attempts to remove it */
         1109   if (model) {
         1110     tree_view->model = model;
         1111     model->view = tree_view;
         1112   }
         1113 }
         1114 
         1115 struct ListStoreSortData {
         1116   GtkTreeIterCompareFunc sort_func;
         1117   GtkListStore *store;
         1118   gboolean reversed;
         1119 };
         1120 
         1121 static gint tree_view_sort_func(gconstpointer a, gconstpointer b, gpointer data){
         1122   /* Map from sorting an array of guint indices into sorting a GtkListStore */
         1123   struct ListStoreSortData *d = data;
         1124   const guint *inda = a, *indb = b;
         1125   if (d->reversed) {
         1126     return d->sort_func(d->store, (guint*)indb, (guint*)inda, NULL);
         1127   } else {
         1128     return d->sort_func(d->store, (guint*)inda, (guint*)indb, NULL);
         1129   }
         1130 }
         1131 
         1132 void gtk_tree_view_sort(GtkTreeView *tv)
         1133 {
         1134   GtkListStore *model = tv->model;
         1135   struct ListStoreSortData data;
         1136   HWND hWnd;
         1137   GArray *inds, *revinds;
         1138   guint i;
         1139   GArray *newrows;
         1140   if (!model || !model->need_sort
         1141       || model->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) {
         1142     return;
         1143   }
         1144 
         1145   /* Before sort, inds[row] = row */
         1146   inds = g_array_sized_new(FALSE, FALSE, sizeof(guint), model->rows->len);
         1147   for (i = 0; i < model->rows->len; ++i) {
         1148     g_array_append_val(inds, i);
         1149   }
         1150   data.sort_func = g_array_index(model->sort_func, GtkTreeIterCompareFunc,
         1151                                  model->sort_column_id);
         1152   data.store = model;
         1153   data.reversed = model->sort_order == GTK_SORT_DESCENDING;
         1154   g_array_sort_with_data(inds, tree_view_sort_func, &data);
         1155 
         1156   /* After sort, inds[newrow] = oldrow */
         1157   /* Now we can reconstruct model->rows in the new order */
         1158   newrows = g_array_sized_new(FALSE, FALSE, sizeof(GtkListStoreRow),
         1159                               model->rows->len);
         1160   for (i = 0; i < model->rows->len; ++i) {
         1161     guint oldrow = g_array_index(inds, guint, i);
         1162     g_array_append_val(newrows,
         1163                        g_array_index(model->rows, GtkListStoreRow, oldrow));
         1164   }
         1165 
         1166   /* Make revinds[oldrow] = newrow */
         1167   revinds = g_array_sized_new(FALSE, FALSE, sizeof(guint), model->rows->len);
         1168   g_array_set_size(revinds, model->rows->len);
         1169   for (i = 0; i < model->rows->len; ++i) {
         1170     guint oldrow = g_array_index(inds, guint, i);
         1171     g_array_index(revinds, guint, oldrow) = i;
         1172   }
         1173 
         1174   /* Update selection (only works for a single selection currently) */
         1175   if (tv->selection) {
         1176     guint oldrow = GPOINTER_TO_UINT(tv->selection->data);
         1177     guint newrow = g_array_index(revinds, guint, oldrow);
         1178     if (oldrow != newrow) {
         1179       gtk_tree_selection_unselect_path(tv, &oldrow);
         1180       gtk_tree_selection_select_path(tv, &newrow);
         1181     }
         1182   }
         1183 
         1184   /* No need to free the old row data since the new structure takes ownership */
         1185   g_array_free(model->rows, TRUE);
         1186   model->rows = newrows;
         1187 
         1188   g_array_free(inds, TRUE);
         1189   g_array_free(revinds, TRUE);
         1190   model->need_sort = FALSE;
         1191 
         1192   hWnd = GTK_WIDGET(tv)->hWnd;
         1193   if (hWnd)
         1194     InvalidateRect(hWnd, NULL, FALSE);
         1195 }
         1196 
         1197 GtkTreeModel *gtk_tree_view_get_model(GtkTreeView *tree_view)
         1198 {
         1199   return tree_view->model;
         1200 }
         1201 
         1202 void gtk_tree_view_set_headers_clickable(GtkTreeView *tree_view,
         1203                                          gboolean setting)
         1204 {
         1205   tree_view->headers_clickable = setting;
         1206 }
         1207 
         1208 /* These are noops; we only use these for GtkListStore, which should always
         1209    be owned (and thus freed) by our GtkTreeView */
         1210 void g_object_unref(gpointer object)
         1211 {
         1212 }
         1213 
         1214 gpointer g_object_ref(gpointer object)
         1215 {
         1216   return object;
         1217 }
         1218 
         1219 #else /* for systems with GTK+ */
         1220 
         1221 GtkWidget *gtk_scrolled_tree_view_new(GtkWidget **pack_widg)
         1222 {
         1223   GtkWidget *scrollwin, *clist;
         1224 
         1225   clist = gtk_tree_view_new();
         1226   scrollwin = gtk_scrolled_window_new(NULL, NULL);
         1227   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
         1228                                  GTK_POLICY_AUTOMATIC,
         1229                                  GTK_POLICY_AUTOMATIC);
         1230   gtk_container_add(GTK_CONTAINER(scrollwin), clist);
         1231   *pack_widg = scrollwin;
         1232   return clist;
         1233 }
         1234 
         1235 #endif