|
Packit |
76ec6a |
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
Packit |
76ec6a |
#include <config.h>
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
#include <glib/gi18n.h>
|
|
Packit |
76ec6a |
#include <glibtop/procopenfiles.h>
|
|
Packit |
76ec6a |
#include <sys/stat.h>
|
|
Packit |
76ec6a |
#include <netdb.h>
|
|
Packit |
76ec6a |
#include <sys/types.h>
|
|
Packit |
76ec6a |
#include <sys/socket.h>
|
|
Packit |
76ec6a |
#include <netinet/in.h>
|
|
Packit |
76ec6a |
#include <arpa/inet.h>
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
#include "application.h"
|
|
Packit |
76ec6a |
#include "openfiles.h"
|
|
Packit |
76ec6a |
#include "proctable.h"
|
|
Packit |
76ec6a |
#include "util.h"
|
|
Packit |
76ec6a |
#include "settings-keys.h"
|
|
Packit |
76ec6a |
#include "legacy/treeview.h"
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
#ifndef NI_IDN
|
|
Packit |
76ec6a |
#define NI_IDN 0
|
|
Packit |
76ec6a |
#endif
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
enum
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
COL_FD,
|
|
Packit |
76ec6a |
COL_TYPE,
|
|
Packit |
76ec6a |
COL_OBJECT,
|
|
Packit |
76ec6a |
COL_OPENFILE_STRUCT,
|
|
Packit |
76ec6a |
NUM_OPENFILES_COL
|
|
Packit |
76ec6a |
};
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static const char*
|
|
Packit |
76ec6a |
get_type_name(enum glibtop_file_type t)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
switch(t)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_FILE:
|
|
Packit |
76ec6a |
return _("file");
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_PIPE:
|
|
Packit |
76ec6a |
return _("pipe");
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_INET6SOCKET:
|
|
Packit |
76ec6a |
return _("IPv6 network connection");
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_INETSOCKET:
|
|
Packit |
76ec6a |
return _("IPv4 network connection");
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_LOCALSOCKET:
|
|
Packit |
76ec6a |
return _("local socket");
|
|
Packit |
76ec6a |
default:
|
|
Packit |
76ec6a |
return _("unknown type");
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static char *
|
|
Packit |
76ec6a |
friendlier_hostname(const char *addr_str, int port)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
struct addrinfo hints = { };
|
|
Packit |
76ec6a |
struct addrinfo *res = NULL;
|
|
Packit |
76ec6a |
char hostname[NI_MAXHOST];
|
|
Packit |
76ec6a |
char service[NI_MAXSERV];
|
|
Packit |
76ec6a |
char port_str[6];
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
if (!addr_str[0]) return g_strdup("");
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
snprintf(port_str, sizeof port_str, "%d", port);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
hints.ai_family = AF_UNSPEC;
|
|
Packit |
76ec6a |
hints.ai_socktype = SOCK_STREAM;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
if (getaddrinfo(addr_str, port_str, &hints, &res))
|
|
Packit |
76ec6a |
goto failsafe;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
if (getnameinfo(res->ai_addr, res->ai_addrlen, hostname,
|
|
Packit |
76ec6a |
sizeof hostname, service, sizeof service, NI_IDN))
|
|
Packit |
76ec6a |
goto failsafe;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
if (res) freeaddrinfo(res);
|
|
Packit |
76ec6a |
return g_strdup_printf("%s, TCP port %d (%s)", hostname, port, service);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
failsafe:
|
|
Packit |
76ec6a |
if (res) freeaddrinfo(res);
|
|
Packit |
76ec6a |
return g_strdup_printf("%s, TCP port %d", addr_str, port);
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static void
|
|
Packit |
76ec6a |
add_new_files (gpointer key, gpointer value, gpointer data)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
glibtop_open_files_entry *openfiles = static_cast<glibtop_open_files_entry*>(value);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
GtkTreeModel *model = static_cast<GtkTreeModel*>(data);
|
|
Packit |
76ec6a |
GtkTreeIter row;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
char *object;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
switch(openfiles->type)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_FILE:
|
|
Packit |
76ec6a |
object = g_strdup(openfiles->info.file.name);
|
|
Packit |
76ec6a |
break;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_INET6SOCKET:
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_INETSOCKET:
|
|
Packit |
76ec6a |
object = friendlier_hostname(openfiles->info.sock.dest_host,
|
|
Packit |
76ec6a |
openfiles->info.sock.dest_port);
|
|
Packit |
76ec6a |
break;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
case GLIBTOP_FILE_TYPE_LOCALSOCKET:
|
|
Packit |
76ec6a |
object = g_strdup(openfiles->info.localsock.name);
|
|
Packit |
76ec6a |
break;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
default:
|
|
Packit |
76ec6a |
object = g_strdup("");
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_list_store_insert (GTK_LIST_STORE (model), &row, 0);
|
|
Packit |
76ec6a |
gtk_list_store_set (GTK_LIST_STORE (model), &row,
|
|
Packit |
76ec6a |
COL_FD, openfiles->fd,
|
|
Packit |
76ec6a |
COL_TYPE, get_type_name(static_cast<glibtop_file_type>(openfiles->type)),
|
|
Packit |
76ec6a |
COL_OBJECT, object,
|
|
Packit |
76ec6a |
COL_OPENFILE_STRUCT, g_memdup(openfiles, sizeof(*openfiles)),
|
|
Packit |
76ec6a |
-1);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
g_free(object);
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static GList *old_maps = NULL;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static gboolean
|
|
Packit |
76ec6a |
classify_openfiles (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
GHashTable *new_maps = static_cast<GHashTable*>(data);
|
|
Packit |
76ec6a |
GtkTreeIter *old_iter;
|
|
Packit |
76ec6a |
glibtop_open_files_entry *openfiles;
|
|
Packit |
76ec6a |
gchar *old_name;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_tree_model_get (model, iter, 1, &old_name, -1);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
openfiles = static_cast<glibtop_open_files_entry*>(g_hash_table_lookup (new_maps, old_name));
|
|
Packit |
76ec6a |
if (openfiles) {
|
|
Packit |
76ec6a |
g_hash_table_remove (new_maps, old_name);
|
|
Packit |
76ec6a |
g_free (old_name);
|
|
Packit |
76ec6a |
return FALSE;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
old_iter = gtk_tree_iter_copy (iter);
|
|
Packit |
76ec6a |
old_maps = g_list_append (old_maps, old_iter);
|
|
Packit |
76ec6a |
g_free (old_name);
|
|
Packit |
76ec6a |
return FALSE;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static gboolean
|
|
Packit |
76ec6a |
compare_open_files(gconstpointer a, gconstpointer b)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
const glibtop_open_files_entry *o1 = static_cast<const glibtop_open_files_entry *>(a);
|
|
Packit |
76ec6a |
const glibtop_open_files_entry *o2 = static_cast<const glibtop_open_files_entry *>(b);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
/* Falta manejar los diferentes tipos! */
|
|
Packit |
76ec6a |
return (o1->fd == o2->fd) && (o1->type == o2->type); /* XXX! */
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static void
|
|
Packit |
76ec6a |
update_openfiles_dialog (GsmTreeView *tree)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
ProcInfo *info;
|
|
Packit |
76ec6a |
GtkTreeModel *model;
|
|
Packit |
76ec6a |
glibtop_open_files_entry *openfiles;
|
|
Packit |
76ec6a |
glibtop_proc_open_files procmap;
|
|
Packit |
76ec6a |
GHashTable *new_maps;
|
|
Packit |
76ec6a |
guint i;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
pid_t pid = GPOINTER_TO_UINT(static_cast<pid_t*>(g_object_get_data (G_OBJECT (tree), "selected_info")));
|
|
Packit |
76ec6a |
info = GsmApplication::get()->processes.find(pid);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
if (!info)
|
|
Packit |
76ec6a |
return;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
openfiles = glibtop_get_proc_open_files (&procmap, info->pid);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
if (!openfiles)
|
|
Packit |
76ec6a |
return;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
new_maps = static_cast<GHashTable *>(g_hash_table_new_full (g_str_hash, compare_open_files,
|
|
Packit |
76ec6a |
NULL, NULL));
|
|
Packit |
76ec6a |
for (i=0; i < procmap.number; i++)
|
|
Packit |
76ec6a |
g_hash_table_insert (new_maps, openfiles + i, openfiles + i);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_tree_model_foreach (model, classify_openfiles, new_maps);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
g_hash_table_foreach (new_maps, add_new_files, model);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
while (old_maps) {
|
|
Packit |
76ec6a |
GtkTreeIter *iter = static_cast<GtkTreeIter*>(old_maps->data);
|
|
Packit |
76ec6a |
glibtop_open_files_entry *openfiles = NULL;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_tree_model_get (model, iter,
|
|
Packit |
76ec6a |
COL_OPENFILE_STRUCT, &openfiles,
|
|
Packit |
76ec6a |
-1);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_list_store_remove (GTK_LIST_STORE (model), iter);
|
|
Packit |
76ec6a |
gtk_tree_iter_free (iter);
|
|
Packit |
76ec6a |
g_free (openfiles);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
old_maps = g_list_next (old_maps);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
g_hash_table_destroy (new_maps);
|
|
Packit |
76ec6a |
g_free (openfiles);
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static void
|
|
Packit |
76ec6a |
close_openfiles_dialog (GtkDialog *dialog, gint id, gpointer data)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
GsmTreeView *tree = static_cast<GsmTreeView*>(data);
|
|
Packit |
76ec6a |
guint timer;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gsm_tree_view_save_state (tree);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
timer = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (tree), "timer"));
|
|
Packit |
76ec6a |
g_source_remove (timer);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
return ;
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static GsmTreeView *
|
|
Packit |
76ec6a |
create_openfiles_tree (GsmApplication *app)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
GsmTreeView *tree;
|
|
Packit |
76ec6a |
GtkListStore *model;
|
|
Packit |
76ec6a |
GtkTreeViewColumn *column;
|
|
Packit |
76ec6a |
GtkCellRenderer *cell;
|
|
Packit |
76ec6a |
gint i;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
const gchar * const titles[] = {
|
|
Packit |
76ec6a |
/* Translators: "FD" here means "File Descriptor". Please use
|
|
Packit |
76ec6a |
a very short translation if possible, and at most
|
|
Packit |
76ec6a |
2-3 characters for it to be able to fit in the UI. */
|
|
Packit |
76ec6a |
N_("FD"),
|
|
Packit |
76ec6a |
N_("Type"),
|
|
Packit |
76ec6a |
N_("Object")
|
|
Packit |
76ec6a |
};
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
model = gtk_list_store_new (NUM_OPENFILES_COL,
|
|
Packit |
76ec6a |
G_TYPE_INT, /* FD */
|
|
Packit |
76ec6a |
G_TYPE_STRING, /* Type */
|
|
Packit |
76ec6a |
G_TYPE_STRING, /* Object */
|
|
Packit |
76ec6a |
G_TYPE_POINTER /* open_files_entry */
|
|
Packit |
76ec6a |
);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
auto settings = g_settings_get_child (app->settings->gobj (), GSM_SETTINGS_CHILD_OPEN_FILES);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
tree = gsm_tree_view_new (settings, FALSE);
|
|
Packit |
76ec6a |
gtk_tree_view_set_model (GTK_TREE_VIEW (tree), GTK_TREE_MODEL (model));
|
|
Packit |
76ec6a |
g_object_unref (G_OBJECT (model));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
for (i = 0; i < NUM_OPENFILES_COL-1; i++) {
|
|
Packit |
76ec6a |
cell = gtk_cell_renderer_text_new ();
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
switch (i) {
|
|
Packit |
76ec6a |
case COL_FD:
|
|
Packit |
76ec6a |
g_object_set(cell, "xalign", 1.0f, NULL);
|
|
Packit |
76ec6a |
break;
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
column = gtk_tree_view_column_new_with_attributes (_(titles[i]),
|
|
Packit |
76ec6a |
cell,
|
|
Packit |
76ec6a |
"text", i,
|
|
Packit |
76ec6a |
NULL);
|
|
Packit |
76ec6a |
gtk_tree_view_column_set_sort_column_id (column, i);
|
|
Packit |
76ec6a |
gtk_tree_view_column_set_resizable (column, TRUE);
|
|
Packit |
76ec6a |
gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gsm_tree_view_load_state (GSM_TREE_VIEW (tree));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
return tree;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static gboolean
|
|
Packit |
76ec6a |
openfiles_timer (gpointer data)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
GsmTreeView* tree = static_cast<GsmTreeView*>(data);
|
|
Packit |
76ec6a |
GtkTreeModel *model;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
|
|
Packit |
76ec6a |
g_assert(model);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
update_openfiles_dialog (tree);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
return TRUE;
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
static void
|
|
Packit |
76ec6a |
create_single_openfiles_dialog (GtkTreeModel *model, GtkTreePath *path,
|
|
Packit |
76ec6a |
GtkTreeIter *iter, gpointer data)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
GsmApplication *app = static_cast<GsmApplication *>(data);
|
|
Packit |
76ec6a |
GtkDialog *openfilesdialog;
|
|
Packit |
76ec6a |
GtkGrid *cmd_grid;
|
|
Packit |
76ec6a |
GtkLabel *label;
|
|
Packit |
76ec6a |
GtkScrolledWindow *scrolled;
|
|
Packit |
76ec6a |
GsmTreeView *tree;
|
|
Packit |
76ec6a |
ProcInfo *info;
|
|
Packit |
76ec6a |
guint timer;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_tree_model_get (model, iter, COL_POINTER, &info, -1);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
if (!info)
|
|
Packit |
76ec6a |
return;
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
GtkBuilder *builder = gtk_builder_new();
|
|
Packit |
76ec6a |
gtk_builder_add_from_resource (builder, "/org/gnome/gnome-system-monitor/data/openfiles.ui", NULL);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
openfilesdialog = GTK_DIALOG (gtk_builder_get_object (builder, "openfiles_dialog"));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
cmd_grid = GTK_GRID (gtk_builder_get_object (builder, "cmd_grid"));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
label = procman_make_label_for_mmaps_or_ofiles (
|
|
Packit |
76ec6a |
_("_Files opened by process ā%sā (PID %u):"),
|
|
Packit |
76ec6a |
info->name.c_str(),
|
|
Packit |
76ec6a |
info->pid);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_container_add (GTK_CONTAINER (cmd_grid), GTK_WIDGET (label));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
scrolled = GTK_SCROLLED_WINDOW (gtk_builder_get_object (builder, "scrolled"));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
tree = create_openfiles_tree (app);
|
|
Packit |
76ec6a |
gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (tree));
|
|
Packit |
76ec6a |
g_object_set_data (G_OBJECT (tree), "selected_info", GUINT_TO_POINTER (info->pid));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
g_signal_connect (G_OBJECT (openfilesdialog), "response",
|
|
Packit |
76ec6a |
G_CALLBACK (close_openfiles_dialog), tree);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_builder_connect_signals (builder, NULL);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
gtk_window_set_transient_for (GTK_WINDOW (openfilesdialog), GTK_WINDOW (GsmApplication::get()->main_window));
|
|
Packit |
76ec6a |
gtk_widget_show_all (GTK_WIDGET (openfilesdialog));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
timer = g_timeout_add_seconds (5, openfiles_timer, tree);
|
|
Packit |
76ec6a |
g_object_set_data (G_OBJECT (tree), "timer", GUINT_TO_POINTER (timer));
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
update_openfiles_dialog (tree);
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
g_object_unref (G_OBJECT (builder));
|
|
Packit |
76ec6a |
}
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
|
|
Packit |
76ec6a |
void
|
|
Packit |
76ec6a |
create_openfiles_dialog (GsmApplication *app)
|
|
Packit |
76ec6a |
{
|
|
Packit |
76ec6a |
gtk_tree_selection_selected_foreach (app->selection, create_single_openfiles_dialog,
|
|
Packit |
76ec6a |
app);
|
|
Packit |
76ec6a |
}
|