/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #define _FILE_OFFSET_BITS 64 /* allow selection of large files */ #include #include #include #include #include #include #include #include "fe-gtk.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../common/xchat.h" #include "../common/fe.h" #include "../common/util.h" #include "gtkutil.h" #include "pixmaps.h" #ifdef WIN32 #include "../common/fe.h" #include "../common/thread.h" #endif /* gtkutil.c, just some gtk wrappers */ extern void path_part (char *file, char *path, int pathlen); struct file_req { GtkWidget *dialog; void *userdata; filereqcallback callback; int flags; /* FRF_* flags */ #ifdef WIN32 int multiple; thread *th; char *title; /* native locale */ char *filter; #endif }; static char last_dir[256] = ""; static void gtkutil_file_req_destroy (GtkWidget * wid, struct file_req *freq) { freq->callback (freq->userdata, NULL); free (freq); } static void gtkutil_check_file (char *file, struct file_req *freq) { struct stat st; int axs = FALSE; path_part (file, last_dir, sizeof (last_dir)); /* check if the file is readable or writable */ if (freq->flags & FRF_WRITE) { if (access (last_dir, W_OK) == 0) axs = TRUE; } else { if (stat (file, &st) != -1) { if (!S_ISDIR (st.st_mode) || (freq->flags & FRF_CHOOSEFOLDER)) axs = TRUE; } } if (axs) { char *utf8_file; /* convert to UTF8. It might be converted back to locale by server.c's g_convert */ utf8_file = xchat_filename_to_utf8 (file, -1, NULL, NULL, NULL); if (utf8_file) { freq->callback (freq->userdata, utf8_file); g_free (utf8_file); } else { fe_message ("Filename encoding is corrupt.", FE_MSG_ERROR); } } else { if (freq->flags & FRF_WRITE) fe_message (_("Cannot write to that file."), FE_MSG_ERROR); else fe_message (_("Cannot read that file."), FE_MSG_ERROR); } } static void gtkutil_file_req_done (GtkWidget * wid, struct file_req *freq) { GSList *files, *cur; GtkFileChooser *fs = GTK_FILE_CHOOSER (freq->dialog); if (freq->flags & FRF_MULTIPLE) { files = cur = gtk_file_chooser_get_filenames (fs); while (cur) { gtkutil_check_file (cur->data, freq); g_free (cur->data); cur = cur->next; } if (files) g_slist_free (files); } else { if (freq->flags & FRF_CHOOSEFOLDER) gtkutil_check_file (gtk_file_chooser_get_current_folder (fs), freq); else gtkutil_check_file (gtk_file_chooser_get_filename (fs), freq); } /* this should call the "destroy" cb, where we free(freq) */ gtk_widget_destroy (freq->dialog); } static void gtkutil_file_req_response (GtkWidget *dialog, gint res, struct file_req *freq) { switch (res) { case GTK_RESPONSE_ACCEPT: gtkutil_file_req_done (dialog, freq); break; case GTK_RESPONSE_CANCEL: /* this should call the "destroy" cb, where we free(freq) */ gtk_widget_destroy (freq->dialog); } } #ifdef WIN32 static int win32_openfile (char *file_buf, int file_buf_len, char *title_text, char *filter, int multiple) { OPENFILENAME o; memset (&o, 0, sizeof (o)); o.lStructSize = sizeof (o); o.lpstrFilter = filter; o.lpstrFile = file_buf; o.nMaxFile = file_buf_len; o.lpstrTitle = title_text; o.Flags = 0x02000000 | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_LONGNAMES | OFN_NONETWORKBUTTON; if (multiple) { o.Flags |= OFN_ALLOWMULTISELECT; } return GetOpenFileName (&o); } static int win32_savefile (char *file_buf, int file_buf_len, char *title_text, char *filter, int multiple) { /* we need the filter to get the default filename. it is from fe-gtk.c (fe_confirm); * but that filter is actually the whole filename, so apply an empty filter and all good. * in win32_thread2 we copy the filter ( = the filename) after the last dir into our * LPTSTR file buffer to make it actually work. the docs for this amazingly retard api: * * http://msdn.microsoft.com/en-us/library/ms646839%28VS.85%29.aspx */ OPENFILENAME o; memset (&o, 0, sizeof (o)); o.lStructSize = sizeof (o); o.lpstrFilter = "All files\0*.*\0\0"; o.lpstrFile = file_buf; o.nMaxFile = file_buf_len; o.lpstrTitle = title_text; o.Flags = 0x02000000 | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_LONGNAMES | OFN_NONETWORKBUTTON; if (multiple) { o.Flags |= OFN_ALLOWMULTISELECT; } return GetSaveFileName (&o); } static void * win32_thread (struct file_req *freq) { char buf[1024 + 32]; char file[1024]; memset (file, 0, sizeof (file)); safe_strcpy (file, last_dir, sizeof (file)); if (win32_openfile (file, sizeof (file), freq->title, freq->filter, freq->multiple)) { if (freq->multiple) { char *f = file; if (f[strlen (f) + 1] == 0) /* only selected one file */ { snprintf (buf, sizeof (buf), "1\n%s\n", file); write (freq->th->pipe_fd[1], buf, strlen (buf)); } else { f += strlen (f) + 1; /* skip first, it's only the dir */ while (f[0]) { snprintf (buf, sizeof (buf), "1\n%s\\%s\n", /*dir!*/file, f); write (freq->th->pipe_fd[1], buf, strlen (buf)); f += strlen (f) + 1; } } } else { snprintf (buf, sizeof (buf), "1\n%s\n", file); write (freq->th->pipe_fd[1], buf, strlen (buf)); } } write (freq->th->pipe_fd[1], "0\n", 2); Sleep (2000); return NULL; } static void * win32_thread2 (struct file_req *freq) { char buf[1024 + 32]; char file[1024]; memset (file, 0, sizeof (file)); safe_strcpy (file, last_dir, sizeof (file)); safe_strcpy (file, freq->filter, sizeof (file)); if (win32_savefile (file, sizeof (file), freq->title, NULL, freq->multiple)) { if (freq->multiple) { char *f = file; if (f[strlen (f) + 1] == 0) /* only selected one file */ { snprintf (buf, sizeof (buf), "1\n%s\n", file); write (freq->th->pipe_fd[1], buf, strlen (buf)); } else { f += strlen (f) + 1; /* skip first, it's only the dir */ while (f[0]) { snprintf (buf, sizeof (buf), "1\n%s\\%s\n", /*dir!*/file, f); write (freq->th->pipe_fd[1], buf, strlen (buf)); f += strlen (f) + 1; } } } else { snprintf (buf, sizeof (buf), "1\n%s\n", file); write (freq->th->pipe_fd[1], buf, strlen (buf)); } } write (freq->th->pipe_fd[1], "0\n", 2); Sleep (2000); return NULL; } static gboolean win32_close_pipe (int fd) { close (fd); return 0; } static gboolean win32_read_thread (GIOChannel *source, GIOCondition cond, struct file_req *freq) { char buf[512]; char *file; waitline2 (source, buf, sizeof buf); switch (buf[0]) { case '0': /* filedialog has closed */ freq->callback (freq->userdata, NULL); break; case '1': /* got a filename! */ waitline2 (source, buf, sizeof buf); file = g_filename_to_utf8 (buf, -1, 0, 0, 0); freq->callback (freq->userdata, file); g_free (file); return TRUE; } /* it doesn't work to close them here, because of the weird way giowin32 works. We must _return_ before closing them */ g_timeout_add(3000, (GSourceFunc)win32_close_pipe, freq->th->pipe_fd[0]); g_timeout_add(2000, (GSourceFunc)win32_close_pipe, freq->th->pipe_fd[1]); g_free (freq->title); free (freq->th); free (freq); return FALSE; } #endif void gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter, int flags) { struct file_req *freq; GtkWidget *dialog; extern char *get_xdir_fs (void); #ifdef WIN32 if (!(flags & FRF_WRITE)) { freq = malloc (sizeof (struct file_req)); freq->th = thread_new (); freq->flags = 0; freq->multiple = (flags & FRF_MULTIPLE); freq->callback = callback; freq->userdata = userdata; freq->title = g_locale_from_utf8 (title, -1, 0, 0, 0); if (!filter) { freq->filter = "All files\0*.*\0" "Executables\0*.exe\0" "ZIP files\0*.zip\0\0"; } else { freq->filter = filter; } thread_start (freq->th, win32_thread, freq); fe_input_add (freq->th->pipe_fd[0], FIA_FD|FIA_READ, win32_read_thread, freq); return; } else { freq = malloc (sizeof (struct file_req)); freq->th = thread_new (); freq->flags = 0; freq->multiple = (flags & FRF_MULTIPLE); freq->callback = callback; freq->userdata = userdata; freq->title = g_locale_from_utf8 (title, -1, 0, 0, 0); if (!filter) { freq->filter = "All files\0*.*\0\0"; } else { freq->filter = filter; } thread_start (freq->th, win32_thread2, freq); fe_input_add (freq->th->pipe_fd[0], FIA_FD|FIA_READ, win32_read_thread, freq); return; } #endif if (flags & FRF_WRITE) { dialog = gtk_file_chooser_dialog_new (title, NULL, GTK_FILE
/*

  Copyright (c) 2010 Samuel Lidén Borell <samuel@kodafritt.se>

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

*/

#ifndef PLUGIN_HEXCHAT_H
#define PLUGIN_HEXCHAT_H

gchar *get_config_filename();

#endif
_container_add (GTK_CONTAINER (box), win); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win), GTK_POLICY_AUTOMATIC, policy); gtk_widget_show (win); if (titles) clist = gtk_clist_new_with_titles (columns, titles); else clist = gtk_clist_new (columns); gtk_clist_set_selection_mode (GTK_CLIST (clist), selection_mode); gtk_clist_column_titles_passive (GTK_CLIST (clist)); gtk_container_add (GTK_CONTAINER (win), clist); if (select_callback) { g_signal_connect (G_OBJECT (clist), "select_row", G_CALLBACK (select_callback), select_userdata); } if (unselect_callback) { g_signal_connect (G_OBJECT (clist), "unselect_row", G_CALLBACK (unselect_callback), unselect_userdata); } gtk_widget_show (clist); return clist; } int gtkutil_clist_selection (GtkWidget * clist) { if (GTK_CLIST (clist)->selection) return GPOINTER_TO_INT(GTK_CLIST (clist)->selection->data); return -1; } static int int_compare (const int * elem1, const int * elem2) { return (*elem1) - (*elem2); } int gtkutil_clist_multiple_selection (GtkWidget * clist, int ** rows, const int max_rows) { int i = 0; GList *tmp_clist; *rows = malloc (sizeof (int) * max_rows ); memset( *rows, -1, max_rows * sizeof(int) ); for( tmp_clist = GTK_CLIST(clist)->selection; tmp_clist && i < max_rows; tmp_clist = tmp_clist->next, i++) { (*rows)[i] = GPOINTER_TO_INT( tmp_clist->data ); } qsort(*rows, i, sizeof(int), (void *)int_compare); return i; } void add_tip (GtkWidget * wid, char *text) { static GtkTooltips *tip = NULL; if (!tip) tip = gtk_tooltips_new (); gtk_tooltips_set_tip (tip, wid, text, 0); } void show_and_unfocus (GtkWidget * wid) { GTK_WIDGET_UNSET_FLAGS (wid, GTK_CAN_FOCUS); gtk_widget_show (wid); } void gtkutil_set_icon (GtkWidget *win) { gtk_window_set_icon (GTK_WINDOW (win), pix_xchat); } extern GtkWidget *parent_window; /* maingui.c */ GtkWidget * gtkutil_window_new (char *title, char *role, int width, int height, int flags) { GtkWidget *win; win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtkutil_set_icon (win); #ifdef WIN32 gtk_window_set_wmclass (GTK_WINDOW (win), "XChat", "xchat"); #endif gtk_window_set_title (GTK_WINDOW (win), title); gtk_window_set_default_size (GTK_WINDOW (win), width, height); gtk_window_set_role (GTK_WINDOW (win), role); if (flags & 1) gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_MOUSE); if ((flags & 2) && parent_window) { gtk_window_set_type_hint (GTK_WINDOW (win), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent_window)); } return win; } /* pass NULL as selection to paste to both clipboard & X11 text */ void gtkutil_copy_to_clipboard (GtkWidget *widget, GdkAtom selection, const gchar *str) { GtkWidget *win; GtkClipboard *clip, *clip2; win = gtk_widget_get_toplevel (GTK_WIDGET (widget)); if (GTK_WIDGET_TOPLEVEL (win)) { int len = strlen (str); if (selection) { clip = gtk_widget_get_clipboard (win, selection); gtk_clipboard_set_text (clip, str, len); } else { /* copy to both primary X selection and clipboard */ clip = gtk_widget_get_clipboard (win, GDK_SELECTION_PRIMARY); clip2 = gtk_widget_get_clipboard (win, GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text (clip, str, len); gtk_clipboard_set_text (clip2, str, len); } } } /* Treeview util functions */ GtkWidget * gtkutil_treeview_new (GtkWidget *box, GtkTreeModel *model, GtkTreeCellDataFunc mapper, ...) { GtkWidget *win, *view; GtkCellRenderer *renderer = NULL; GtkTreeViewColumn *col; va_list args; int col_id = 0; GType type; char *title, *attr; win = gtk_scrolled_window_new (0, 0); gtk_container_add (GTK_CONTAINER (box), win); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show (win); view = gtk_tree_view_new_with_model (model); /* the view now has a ref on the model, we can unref it */ g_object_unref (G_OBJECT (model)); gtk_container_add (GTK_CONTAINER (win), view); va_start (args, mapper); for (col_id = va_arg (args, int); col_id != -1; col_id = va_arg (args, int)) { type = gtk_tree_model_get_column_type (model, col_id); switch (type) { case G_TYPE_BOOLEAN: renderer = gtk_cell_renderer_toggle_new (); attr = "active"; break; case G_TYPE_STRING: /* fall through */ default: renderer = gtk_cell_renderer_text_new (); attr = "text"; break; } title = va_arg (args, char *); if (mapper) /* user-specified function to set renderer attributes */ { col = gtk_tree_view_column_new_with_attributes (title, renderer, NULL); gtk_tree_view_column_set_cell_data_func (col, renderer, mapper, GINT_TO_POINTER (col_id), NULL); } else { /* just set the typical attribute for this type of renderer */ col = gtk_tree_view_column_new_with_attributes (title, renderer, attr, col_id, NULL); } gtk_tree_view_append_column (GTK_TREE_VIEW (view), col); } va_end (args); return view; } gboolean gtkutil_treemodel_string_to_iter (GtkTreeModel *model, gchar *pathstr, GtkTreeIter *iter_ret) { GtkTreePath *path = gtk_tree_path_new_from_string (pathstr); gboolean success; success = gtk_tree_model_get_iter (model, iter_ret, path); gtk_tree_path_free (path); return success; } /*gboolean gtkutil_treeview_get_selected_iter (GtkTreeView *view, GtkTreeIter *iter_ret) { GtkTreeModel *store; GtkTreeSelection *select; select = gtk_tree_view_get_selection (view); return gtk_tree_selection_get_selected (select, &store, iter_ret); }*/ gboolean gtkutil_treeview_get_selected (GtkTreeView *view, GtkTreeIter *iter_ret, ...) { GtkTreeModel *store; GtkTreeSelection *select; gboolean has_selected; va_list args; select = gtk_tree_view_get_selection (view); has_selected = gtk_tree_selection_get_selected (select, &store, iter_ret); if (has_selected) { va_start (args, iter_ret); gtk_tree_model_get_valist (store, iter_ret, args); va_end (args); } return has_selected; }