Merge lp:~noskcaj/ubuntu/vivid/thunar/1.6.4 into lp:ubuntu/vivid/thunar
- Vivid (15.04)
- 1.6.4
- Merge into vivid
Proposed by
Jackson Doak
Status: | Needs review |
---|---|
Proposed branch: | lp:~noskcaj/ubuntu/vivid/thunar/1.6.4 |
Merge into: | lp:ubuntu/vivid/thunar |
Diff against target: |
212897 lines (+58226/-66350) 175 files modified
.pc/02_fix-default-application-selection.patch/thunar/thunar-file.c (+0/-4064) .pc/applied-patches (+0/-5) .pc/git-force-toolbr-icons.patch/thunar/thunar-window.c (+0/-3846) .pc/git-save-keyboard-shortcuts.patch/thunar/thunar-application.c (+0/-2067) .pc/git-xfdesktop-4.11.patch/plugins/thunar-wallpaper/twp-provider.c (+0/-307) .pc/gtk3-bookmarks.patch/thunar/thunar-gio-extensions.c (+0/-587) .pc/gtk3-bookmarks.patch/thunar/thunar-shortcuts-model.c (+0/-2214) .pc/gtk3-bookmarks.patch/thunar/thunar-util.c (+0/-485) .pc/menu-icon-tweaks.patch/thunar/thunar-standard-view.c (+9/-2) .pc/menu-icon-tweaks.patch/thunar/thunar-tree-view.c (+1/-0) .pc/menu-icon-tweaks.patch/thunar/thunar-window.c (+1/-0) ChangeLog (+2473/-0) INSTALL (+3/-3) Makefile.am (+26/-3) Makefile.in (+147/-41) NEWS (+29/-0) aclocal.m4 (+289/-154) config.guess (+99/-214) config.h.in (+0/-3) config.sub (+33/-24) configure (+431/-285) configure.ac (+4/-4) debian/changelog (+10/-0) debian/patches/0001-Don-t-copy-templates-but-create-them-bug-8312.patch (+0/-256) debian/patches/02_fix-default-application-selection.patch (+0/-39) debian/patches/git-force-toolbr-icons.patch (+0/-18) debian/patches/git-save-keyboard-shortcuts.patch (+0/-138) debian/patches/git-xfdesktop-4.11.patch (+0/-188) debian/patches/gtk3-bookmarks.patch (+0/-80) debian/patches/series (+0/-5) debian/thunar.install (+2/-0) depcomp (+2/-1) docs/Makefile.am (+1/-2) docs/Makefile.in (+53/-26) docs/README.thunarrc (+0/-253) docs/Thunar.1 (+2/-2) docs/design/Makefile.in (+46/-17) docs/papers/Makefile.in (+46/-17) docs/reference/Makefile.in (+52/-24) docs/reference/thunarx/Makefile.am (+2/-1) docs/reference/thunarx/Makefile.in (+134/-75) docs/reference/thunarx/html/ThunarxFileInfo.html (+798/-571) docs/reference/thunarx/html/ThunarxMenuProvider.html (+311/-243) docs/reference/thunarx/html/ThunarxPreferencesProvider.html (+131/-97) docs/reference/thunarx/html/ThunarxPropertyPage.html (+329/-227) docs/reference/thunarx/html/ThunarxPropertyPageProvider.html (+148/-115) docs/reference/thunarx/html/ThunarxProviderFactory.html (+125/-102) docs/reference/thunarx/html/ThunarxProviderPlugin.html (+654/-490) docs/reference/thunarx/html/ThunarxRenamer.html (+635/-475) docs/reference/thunarx/html/ThunarxRenamerProvider.html (+122/-95) docs/reference/thunarx/html/index.html (+4/-4) docs/reference/thunarx/html/index.sgml (+70/-40) docs/reference/thunarx/html/ix01.html (+109/-109) docs/reference/thunarx/html/style.css (+317/-107) docs/reference/thunarx/html/thunarx-Variables-and-functions-to-check-the-library-version.html (+208/-170) docs/reference/thunarx/html/thunarx-abstraction-layer.html (+9/-9) docs/reference/thunarx/html/thunarx-fundamentals.html (+9/-9) docs/reference/thunarx/html/thunarx-overview.html (+9/-9) docs/reference/thunarx/html/thunarx-providers.html (+9/-9) docs/reference/thunarx/html/thunarx-using-extensions.html (+9/-9) docs/reference/thunarx/html/thunarx-writing-extensions-advanced-topics.html (+11/-11) docs/reference/thunarx/html/thunarx-writing-extensions-getting-started.html (+10/-10) docs/reference/thunarx/html/thunarx-writing-extensions.html (+13/-13) docs/reference/thunarx/html/thunarx.devhelp2 (+58/-58) docs/reference/thunarx/version.xml (+1/-1) examples/Makefile.in (+52/-24) examples/tex-open-terminal/Makefile.in (+49/-19) gtk-doc.make (+77/-42) icons/128x128/Makefile.in (+46/-17) icons/16x16/Makefile.in (+46/-17) icons/24x24/Makefile.in (+46/-17) icons/48x48/Makefile.in (+46/-17) icons/64x64/Makefile.in (+46/-17) icons/Makefile.in (+52/-24) icons/scalable/Makefile.in (+46/-17) ltmain.sh (+19/-13) missing (+2/-2) org.xfce.thunar.policy.in.in (+37/-0) pixmaps/Makefile.in (+46/-17) plugins/Makefile.in (+52/-24) plugins/thunar-apr/Makefile.in (+49/-19) plugins/thunar-sbr/Makefile.in (+49/-19) plugins/thunar-sendto-email/Makefile.in (+49/-19) plugins/thunar-tpa/Makefile.in (+49/-19) plugins/thunar-tpa/thunar-tpa.c (+9/-11) plugins/thunar-uca/Makefile.in (+49/-19) plugins/thunar-wallpaper/Makefile.in (+49/-19) po/POTFILES.in (+2/-0) po/ar.po (+511/-807) po/ast.po (+938/-1327) po/be.po (+768/-1215) po/bg.po (+590/-885) po/bn.po (+602/-854) po/ca.po (+322/-741) po/cs.po (+632/-1066) po/da.po (+520/-901) po/de.po (+683/-1106) po/el.po (+717/-984) po/en_AU.po (+3597/-0) po/en_GB.po (+544/-927) po/eo.po (+666/-1129) po/es.po (+478/-670) po/et.po (+984/-1409) po/eu.po (+533/-918) po/fi.po (+618/-879) po/fr.po (+723/-1132) po/gl.po (+617/-1057) po/he.po (+394/-724) po/hr.po (+552/-658) po/hu.po (+417/-570) po/id.po (+299/-685) po/is.po (+939/-1426) po/it.po (+598/-1088) po/ja.po (+469/-585) po/kk.po (+436/-791) po/ko.po (+486/-523) po/lt.po (+613/-875) po/lv.po (+621/-1034) po/ms.po (+3563/-0) po/nb.po (+632/-1032) po/nl.po (+359/-784) po/nn.po (+364/-772) po/oc.po (+3591/-0) po/pa.po (+621/-898) po/pl.po (+557/-967) po/pt.po (+541/-758) po/pt_BR.po (+607/-1009) po/ro.po (+539/-771) po/ru.po (+328/-504) po/sk.po (+642/-1058) po/sq.po (+646/-1072) po/sr.po (+577/-765) po/sv.po (+717/-1142) po/te.po (+720/-834) po/th.po (+3564/-0) po/tr.po (+749/-1141) po/ug.po (+577/-816) po/uk.po (+542/-903) po/ur.po (+645/-1036) po/ur_PK.po (+645/-1036) po/vi.po (+1027/-1225) po/zh_CN.po (+543/-675) po/zh_HK.po (+3551/-0) po/zh_TW.po (+599/-896) test-driver (+16/-4) thunar.appdata.xml.in (+24/-0) thunar/Makefile.in (+49/-19) thunar/main.c (+4/-4) thunar/thunar-create-dialog.c (+1/-1) thunar/thunar-dialogs.c (+6/-4) thunar/thunar-dnd.c (+1/-1) thunar/thunar-file.c (+135/-50) thunar/thunar-file.h (+141/-139) thunar/thunar-folder.c (+7/-2) thunar/thunar-gio-extensions.c (+14/-7) thunar/thunar-history.c (+2/-0) thunar/thunar-icon-renderer.c (+43/-2) thunar/thunar-io-jobs-util.c (+39/-39) thunar/thunar-list-model.c (+85/-6) thunar/thunar-preferences-dialog.c (+7/-1) thunar/thunar-preferences.c (+13/-0) thunar/thunar-properties-dialog.c (+2/-4) thunar/thunar-renamer-model.c (+3/-4) thunar/thunar-session-client.c (+1/-0) thunar/thunar-size-label.c (+22/-9) thunar/thunar-standard-view.c (+9/-2) thunar/thunar-text-renderer.c (+2/-1) thunar/thunar-thumbnail-cache.c (+128/-128) thunar/thunar-thumbnailer.c (+2/-0) thunar/thunar-transfer-job.c (+30/-17) thunar/thunar-tree-view.c (+1/-0) thunar/thunar-util.c (+112/-0) thunar/thunar-util.h (+2/-0) thunarx/Makefile.in (+50/-20) thunarx/thunarx-config.h (+1/-1) |
To merge this branch: | bzr merge lp:~noskcaj/ubuntu/vivid/thunar/1.6.4 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Holbach (community) | Needs Fixing | ||
Review via email: mp+245533@code.launchpad.net |
Commit message
Description of the change
New upstream release. Xubuntu 15.04
To post a comment you must log in.
- 100. By Jackson Doak
-
Install appdata and polkit files in thunar deb
- 101. By Jackson Doak
-
02_fix-
default- application- selection. patch, fixed upstream
Unmerged revisions
- 101. By Jackson Doak
-
02_fix-
default- application- selection. patch, fixed upstream - 100. By Jackson Doak
-
Install appdata and polkit files in thunar deb
- 99. By Jackson Doak
-
* New upstream release.
* Dropped patches: git-force-toolbr- icons.patch, gtk3-bookmarks. patch,
git-save-keyboard- shortcuts. patch, git-xfdesktop- 4.11.patch,
02_fix-default- application- selection. patch
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed directory '.pc/02_fix-default-application-selection.patch' | |||
2 | === removed directory '.pc/02_fix-default-application-selection.patch/thunar' | |||
3 | === removed file '.pc/02_fix-default-application-selection.patch/thunar/thunar-file.c' | |||
4 | --- .pc/02_fix-default-application-selection.patch/thunar/thunar-file.c 2014-10-30 11:00:02 +0000 | |||
5 | +++ .pc/02_fix-default-application-selection.patch/thunar/thunar-file.c 1970-01-01 00:00:00 +0000 | |||
6 | @@ -1,4064 +0,0 @@ | |||
7 | 1 | /* vi:set et ai sw=2 sts=2 ts=2: */ | ||
8 | 2 | /*- | ||
9 | 3 | * Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org> | ||
10 | 4 | * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> | ||
11 | 5 | * | ||
12 | 6 | * This program is free software; you can redistribute it and/or | ||
13 | 7 | * modify it under the terms of the GNU General Public License as | ||
14 | 8 | * published by the Free Software Foundation; either version 2 of | ||
15 | 9 | * the License, or (at your option) any later version. | ||
16 | 10 | * | ||
17 | 11 | * This program is distributed in the hope that it will be useful, | ||
18 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | 14 | * GNU General Public License for more details. | ||
21 | 15 | * | ||
22 | 16 | * You should have received a copy of the GNU General Public | ||
23 | 17 | * License along with this program; if not, write to the Free | ||
24 | 18 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
25 | 19 | * Boston, MA 02110-1301, USA. | ||
26 | 20 | */ | ||
27 | 21 | |||
28 | 22 | #ifdef HAVE_CONFIG_H | ||
29 | 23 | #include <config.h> | ||
30 | 24 | #endif | ||
31 | 25 | |||
32 | 26 | #ifdef HAVE_SYS_TYPES_H | ||
33 | 27 | #include <sys/types.h> | ||
34 | 28 | #endif | ||
35 | 29 | |||
36 | 30 | #ifdef HAVE_SYS_STAT_H | ||
37 | 31 | #include <sys/stat.h> | ||
38 | 32 | #endif | ||
39 | 33 | |||
40 | 34 | #ifdef HAVE_ERRNO_H | ||
41 | 35 | #include <errno.h> | ||
42 | 36 | #endif | ||
43 | 37 | #ifdef HAVE_MEMORY_H | ||
44 | 38 | #include <memory.h> | ||
45 | 39 | #endif | ||
46 | 40 | #ifdef HAVE_STDLIB_H | ||
47 | 41 | #include <stdlib.h> | ||
48 | 42 | #endif | ||
49 | 43 | #ifdef HAVE_STRING_H | ||
50 | 44 | #include <string.h> | ||
51 | 45 | #endif | ||
52 | 46 | #ifdef HAVE_TIME_H | ||
53 | 47 | #include <time.h> | ||
54 | 48 | #endif | ||
55 | 49 | #ifdef HAVE_UNISTD_H | ||
56 | 50 | #include <unistd.h> | ||
57 | 51 | #endif | ||
58 | 52 | |||
59 | 53 | #include <gio/gio.h> | ||
60 | 54 | #include <libxfce4ui/libxfce4ui.h> | ||
61 | 55 | |||
62 | 56 | #include <thunarx/thunarx.h> | ||
63 | 57 | |||
64 | 58 | #include <thunar/thunar-application.h> | ||
65 | 59 | #include <thunar/thunar-chooser-dialog.h> | ||
66 | 60 | #include <thunar/thunar-exec.h> | ||
67 | 61 | #include <thunar/thunar-file.h> | ||
68 | 62 | #include <thunar/thunar-file-monitor.h> | ||
69 | 63 | #include <thunar/thunar-gio-extensions.h> | ||
70 | 64 | #include <thunar/thunar-gobject-extensions.h> | ||
71 | 65 | #include <thunar/thunar-private.h> | ||
72 | 66 | #include <thunar/thunar-user.h> | ||
73 | 67 | #include <thunar/thunar-util.h> | ||
74 | 68 | #include <thunar/thunar-dialogs.h> | ||
75 | 69 | #include <thunar/thunar-icon-factory.h> | ||
76 | 70 | |||
77 | 71 | |||
78 | 72 | |||
79 | 73 | /* Dump the file cache every X second, set to 0 to disable */ | ||
80 | 74 | #define DUMP_FILE_CACHE 0 | ||
81 | 75 | |||
82 | 76 | |||
83 | 77 | |||
84 | 78 | /* Signal identifiers */ | ||
85 | 79 | enum | ||
86 | 80 | { | ||
87 | 81 | DESTROY, | ||
88 | 82 | LAST_SIGNAL, | ||
89 | 83 | }; | ||
90 | 84 | |||
91 | 85 | |||
92 | 86 | |||
93 | 87 | static void thunar_file_info_init (ThunarxFileInfoIface *iface); | ||
94 | 88 | static void thunar_file_dispose (GObject *object); | ||
95 | 89 | static void thunar_file_finalize (GObject *object); | ||
96 | 90 | static gchar *thunar_file_info_get_name (ThunarxFileInfo *file_info); | ||
97 | 91 | static gchar *thunar_file_info_get_uri (ThunarxFileInfo *file_info); | ||
98 | 92 | static gchar *thunar_file_info_get_parent_uri (ThunarxFileInfo *file_info); | ||
99 | 93 | static gchar *thunar_file_info_get_uri_scheme (ThunarxFileInfo *file_info); | ||
100 | 94 | static gchar *thunar_file_info_get_mime_type (ThunarxFileInfo *file_info); | ||
101 | 95 | static gboolean thunar_file_info_has_mime_type (ThunarxFileInfo *file_info, | ||
102 | 96 | const gchar *mime_type); | ||
103 | 97 | static gboolean thunar_file_info_is_directory (ThunarxFileInfo *file_info); | ||
104 | 98 | static GFileInfo *thunar_file_info_get_file_info (ThunarxFileInfo *file_info); | ||
105 | 99 | static GFileInfo *thunar_file_info_get_filesystem_info (ThunarxFileInfo *file_info); | ||
106 | 100 | static GFile *thunar_file_info_get_location (ThunarxFileInfo *file_info); | ||
107 | 101 | static void thunar_file_info_changed (ThunarxFileInfo *file_info); | ||
108 | 102 | static gboolean thunar_file_denies_access_permission (const ThunarFile *file, | ||
109 | 103 | ThunarFileMode usr_permissions, | ||
110 | 104 | ThunarFileMode grp_permissions, | ||
111 | 105 | ThunarFileMode oth_permissions); | ||
112 | 106 | static void thunar_file_monitor (GFileMonitor *monitor, | ||
113 | 107 | GFile *path, | ||
114 | 108 | GFile *other_path, | ||
115 | 109 | GFileMonitorEvent event_type, | ||
116 | 110 | gpointer user_data); | ||
117 | 111 | static gboolean thunar_file_load (ThunarFile *file, | ||
118 | 112 | GCancellable *cancellable, | ||
119 | 113 | GError **error); | ||
120 | 114 | static gboolean thunar_file_is_readable (const ThunarFile *file); | ||
121 | 115 | static gboolean thunar_file_same_filesystem (const ThunarFile *file_a, | ||
122 | 116 | const ThunarFile *file_b); | ||
123 | 117 | |||
124 | 118 | |||
125 | 119 | |||
126 | 120 | G_LOCK_DEFINE_STATIC (file_cache_mutex); | ||
127 | 121 | G_LOCK_DEFINE_STATIC (file_content_type_mutex); | ||
128 | 122 | |||
129 | 123 | |||
130 | 124 | |||
131 | 125 | static ThunarUserManager *user_manager; | ||
132 | 126 | static GHashTable *file_cache; | ||
133 | 127 | static guint32 effective_user_id; | ||
134 | 128 | static GQuark thunar_file_watch_quark; | ||
135 | 129 | static guint file_signals[LAST_SIGNAL]; | ||
136 | 130 | |||
137 | 131 | |||
138 | 132 | |||
139 | 133 | #define FLAG_SET_THUMB_STATE(file,new_state) G_STMT_START{ (file)->flags = ((file)->flags & ~THUNAR_FILE_FLAG_THUMB_MASK) | (new_state); }G_STMT_END | ||
140 | 134 | #define FLAG_GET_THUMB_STATE(file) ((file)->flags & THUNAR_FILE_FLAG_THUMB_MASK) | ||
141 | 135 | #define FLAG_SET(file,flag) G_STMT_START{ ((file)->flags |= (flag)); }G_STMT_END | ||
142 | 136 | #define FLAG_UNSET(file,flag) G_STMT_START{ ((file)->flags &= ~(flag)); }G_STMT_END | ||
143 | 137 | #define FLAG_IS_SET(file,flag) (((file)->flags & (flag)) != 0) | ||
144 | 138 | |||
145 | 139 | #define DEFAULT_CONTENT_TYPE "application/octet-stream" | ||
146 | 140 | |||
147 | 141 | |||
148 | 142 | |||
149 | 143 | typedef enum | ||
150 | 144 | { | ||
151 | 145 | THUNAR_FILE_FLAG_THUMB_MASK = 0x03, /* storage for ThunarFileThumbState */ | ||
152 | 146 | THUNAR_FILE_FLAG_IN_DESTRUCTION = 1 << 2, /* for avoiding recursion during destroy */ | ||
153 | 147 | THUNAR_FILE_FLAG_IS_MOUNTED = 1 << 3, /* whether this file is mounted */ | ||
154 | 148 | } | ||
155 | 149 | ThunarFileFlags; | ||
156 | 150 | |||
157 | 151 | struct _ThunarFileClass | ||
158 | 152 | { | ||
159 | 153 | GObjectClass __parent__; | ||
160 | 154 | |||
161 | 155 | /* signals */ | ||
162 | 156 | void (*destroy) (ThunarFile *file); | ||
163 | 157 | }; | ||
164 | 158 | |||
165 | 159 | struct _ThunarFile | ||
166 | 160 | { | ||
167 | 161 | GObject __parent__; | ||
168 | 162 | |||
169 | 163 | /* storage for the file information */ | ||
170 | 164 | GFileInfo *info; | ||
171 | 165 | GFileType kind; | ||
172 | 166 | GFile *gfile; | ||
173 | 167 | gchar *content_type; | ||
174 | 168 | gchar *icon_name; | ||
175 | 169 | |||
176 | 170 | gchar *custom_icon_name; | ||
177 | 171 | gchar *display_name; | ||
178 | 172 | gchar *basename; | ||
179 | 173 | gchar *thumbnail_path; | ||
180 | 174 | |||
181 | 175 | /* sorting */ | ||
182 | 176 | gchar *collate_key; | ||
183 | 177 | gchar *collate_key_nocase; | ||
184 | 178 | |||
185 | 179 | /* flags for thumbnail state etc */ | ||
186 | 180 | ThunarFileFlags flags; | ||
187 | 181 | }; | ||
188 | 182 | |||
189 | 183 | typedef struct | ||
190 | 184 | { | ||
191 | 185 | GFileMonitor *monitor; | ||
192 | 186 | guint watch_count; | ||
193 | 187 | } | ||
194 | 188 | ThunarFileWatch; | ||
195 | 189 | |||
196 | 190 | typedef struct | ||
197 | 191 | { | ||
198 | 192 | ThunarFileGetFunc func; | ||
199 | 193 | gpointer user_data; | ||
200 | 194 | GCancellable *cancellable; | ||
201 | 195 | } | ||
202 | 196 | ThunarFileGetData; | ||
203 | 197 | |||
204 | 198 | static struct | ||
205 | 199 | { | ||
206 | 200 | GUserDirectory type; | ||
207 | 201 | const gchar *icon_name; | ||
208 | 202 | } | ||
209 | 203 | thunar_file_dirs[] = | ||
210 | 204 | { | ||
211 | 205 | { G_USER_DIRECTORY_DESKTOP, "user-desktop" }, | ||
212 | 206 | { G_USER_DIRECTORY_DOCUMENTS, "folder-documents" }, | ||
213 | 207 | { G_USER_DIRECTORY_DOWNLOAD, "folder-download" }, | ||
214 | 208 | { G_USER_DIRECTORY_MUSIC, "folder-music" }, | ||
215 | 209 | { G_USER_DIRECTORY_PICTURES, "folder-pictures" }, | ||
216 | 210 | { G_USER_DIRECTORY_PUBLIC_SHARE, "folder-publicshare" }, | ||
217 | 211 | { G_USER_DIRECTORY_TEMPLATES, "folder-templates" }, | ||
218 | 212 | { G_USER_DIRECTORY_VIDEOS, "folder-videos" } | ||
219 | 213 | }; | ||
220 | 214 | |||
221 | 215 | |||
222 | 216 | |||
223 | 217 | G_DEFINE_TYPE_WITH_CODE (ThunarFile, thunar_file, G_TYPE_OBJECT, | ||
224 | 218 | G_IMPLEMENT_INTERFACE (THUNARX_TYPE_FILE_INFO, thunar_file_info_init)) | ||
225 | 219 | |||
226 | 220 | |||
227 | 221 | |||
228 | 222 | #ifdef G_ENABLE_DEBUG | ||
229 | 223 | #ifdef HAVE_ATEXIT | ||
230 | 224 | static gboolean thunar_file_atexit_registered = FALSE; | ||
231 | 225 | |||
232 | 226 | |||
233 | 227 | |||
234 | 228 | static void | ||
235 | 229 | thunar_file_atexit_foreach (gpointer key, | ||
236 | 230 | gpointer value, | ||
237 | 231 | gpointer user_data) | ||
238 | 232 | { | ||
239 | 233 | gchar *uri; | ||
240 | 234 | |||
241 | 235 | uri = g_file_get_uri (key); | ||
242 | 236 | g_print ("--> %s (%u)\n", uri, G_OBJECT (value)->ref_count); | ||
243 | 237 | if (G_OBJECT (key)->ref_count > 2) | ||
244 | 238 | g_print (" GFile (%u)\n", G_OBJECT (key)->ref_count - 2); | ||
245 | 239 | g_free (uri); | ||
246 | 240 | } | ||
247 | 241 | |||
248 | 242 | |||
249 | 243 | |||
250 | 244 | static void | ||
251 | 245 | thunar_file_atexit (void) | ||
252 | 246 | { | ||
253 | 247 | G_LOCK (file_cache_mutex); | ||
254 | 248 | |||
255 | 249 | if (file_cache == NULL || g_hash_table_size (file_cache) == 0) | ||
256 | 250 | { | ||
257 | 251 | G_UNLOCK (file_cache_mutex); | ||
258 | 252 | return; | ||
259 | 253 | } | ||
260 | 254 | |||
261 | 255 | g_print ("--- Leaked a total of %u ThunarFile objects:\n", | ||
262 | 256 | g_hash_table_size (file_cache)); | ||
263 | 257 | |||
264 | 258 | g_hash_table_foreach (file_cache, thunar_file_atexit_foreach, NULL); | ||
265 | 259 | |||
266 | 260 | g_print ("\n"); | ||
267 | 261 | |||
268 | 262 | G_UNLOCK (file_cache_mutex); | ||
269 | 263 | } | ||
270 | 264 | #endif | ||
271 | 265 | #endif | ||
272 | 266 | |||
273 | 267 | |||
274 | 268 | |||
275 | 269 | #if DUMP_FILE_CACHE | ||
276 | 270 | static void | ||
277 | 271 | thunar_file_cache_dump_foreach (gpointer gfile, | ||
278 | 272 | gpointer value, | ||
279 | 273 | gpointer user_data) | ||
280 | 274 | { | ||
281 | 275 | gchar *name; | ||
282 | 276 | |||
283 | 277 | name = g_file_get_parse_name (G_FILE (gfile)); | ||
284 | 278 | g_print (" %s (%u)\n", name, G_OBJECT (value)->ref_count); | ||
285 | 279 | g_free (name); | ||
286 | 280 | } | ||
287 | 281 | |||
288 | 282 | |||
289 | 283 | |||
290 | 284 | static gboolean | ||
291 | 285 | thunar_file_cache_dump (gpointer user_data) | ||
292 | 286 | { | ||
293 | 287 | G_LOCK (file_cache_mutex); | ||
294 | 288 | |||
295 | 289 | if (file_cache != NULL) | ||
296 | 290 | { | ||
297 | 291 | g_print ("--- %d ThunarFile objects in cache:\n", | ||
298 | 292 | g_hash_table_size (file_cache)); | ||
299 | 293 | |||
300 | 294 | g_hash_table_foreach (file_cache, thunar_file_cache_dump_foreach, NULL); | ||
301 | 295 | |||
302 | 296 | g_print ("\n"); | ||
303 | 297 | } | ||
304 | 298 | |||
305 | 299 | G_UNLOCK (file_cache_mutex); | ||
306 | 300 | |||
307 | 301 | return TRUE; | ||
308 | 302 | } | ||
309 | 303 | #endif | ||
310 | 304 | |||
311 | 305 | |||
312 | 306 | |||
313 | 307 | static void | ||
314 | 308 | thunar_file_class_init (ThunarFileClass *klass) | ||
315 | 309 | { | ||
316 | 310 | GObjectClass *gobject_class; | ||
317 | 311 | |||
318 | 312 | #ifdef G_ENABLE_DEBUG | ||
319 | 313 | #ifdef HAVE_ATEXIT | ||
320 | 314 | if (G_UNLIKELY (!thunar_file_atexit_registered)) | ||
321 | 315 | { | ||
322 | 316 | atexit ((void (*)(void)) thunar_file_atexit); | ||
323 | 317 | thunar_file_atexit_registered = TRUE; | ||
324 | 318 | } | ||
325 | 319 | #endif | ||
326 | 320 | #endif | ||
327 | 321 | |||
328 | 322 | #if DUMP_FILE_CACHE | ||
329 | 323 | g_timeout_add_seconds (DUMP_FILE_CACHE, thunar_file_cache_dump, NULL); | ||
330 | 324 | #endif | ||
331 | 325 | |||
332 | 326 | /* pre-allocate the required quarks */ | ||
333 | 327 | thunar_file_watch_quark = g_quark_from_static_string ("thunar-file-watch"); | ||
334 | 328 | |||
335 | 329 | /* grab a reference on the user manager */ | ||
336 | 330 | user_manager = thunar_user_manager_get_default (); | ||
337 | 331 | |||
338 | 332 | /* determine the effective user id of the process */ | ||
339 | 333 | effective_user_id = geteuid (); | ||
340 | 334 | |||
341 | 335 | gobject_class = G_OBJECT_CLASS (klass); | ||
342 | 336 | gobject_class->dispose = thunar_file_dispose; | ||
343 | 337 | gobject_class->finalize = thunar_file_finalize; | ||
344 | 338 | |||
345 | 339 | /** | ||
346 | 340 | * ThunarFile::destroy: | ||
347 | 341 | * @file : the #ThunarFile instance. | ||
348 | 342 | * | ||
349 | 343 | * Emitted when the system notices that the @file | ||
350 | 344 | * was destroyed. | ||
351 | 345 | **/ | ||
352 | 346 | file_signals[DESTROY] = | ||
353 | 347 | g_signal_new (I_("destroy"), | ||
354 | 348 | G_TYPE_FROM_CLASS (klass), | ||
355 | 349 | G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, | ||
356 | 350 | G_STRUCT_OFFSET (ThunarFileClass, destroy), | ||
357 | 351 | NULL, NULL, | ||
358 | 352 | g_cclosure_marshal_VOID__VOID, | ||
359 | 353 | G_TYPE_NONE, 0); | ||
360 | 354 | } | ||
361 | 355 | |||
362 | 356 | |||
363 | 357 | |||
364 | 358 | static void | ||
365 | 359 | thunar_file_init (ThunarFile *file) | ||
366 | 360 | { | ||
367 | 361 | } | ||
368 | 362 | |||
369 | 363 | |||
370 | 364 | |||
371 | 365 | static void | ||
372 | 366 | thunar_file_info_init (ThunarxFileInfoIface *iface) | ||
373 | 367 | { | ||
374 | 368 | iface->get_name = thunar_file_info_get_name; | ||
375 | 369 | iface->get_uri = thunar_file_info_get_uri; | ||
376 | 370 | iface->get_parent_uri = thunar_file_info_get_parent_uri; | ||
377 | 371 | iface->get_uri_scheme = thunar_file_info_get_uri_scheme; | ||
378 | 372 | iface->get_mime_type = thunar_file_info_get_mime_type; | ||
379 | 373 | iface->has_mime_type = thunar_file_info_has_mime_type; | ||
380 | 374 | iface->is_directory = thunar_file_info_is_directory; | ||
381 | 375 | iface->get_file_info = thunar_file_info_get_file_info; | ||
382 | 376 | iface->get_filesystem_info = thunar_file_info_get_filesystem_info; | ||
383 | 377 | iface->get_location = thunar_file_info_get_location; | ||
384 | 378 | iface->changed = thunar_file_info_changed; | ||
385 | 379 | } | ||
386 | 380 | |||
387 | 381 | |||
388 | 382 | |||
389 | 383 | static void | ||
390 | 384 | thunar_file_dispose (GObject *object) | ||
391 | 385 | { | ||
392 | 386 | ThunarFile *file = THUNAR_FILE (object); | ||
393 | 387 | |||
394 | 388 | /* check that we don't recurse here */ | ||
395 | 389 | if (!FLAG_IS_SET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION)) | ||
396 | 390 | { | ||
397 | 391 | /* emit the "destroy" signal */ | ||
398 | 392 | FLAG_SET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION); | ||
399 | 393 | g_signal_emit (object, file_signals[DESTROY], 0); | ||
400 | 394 | FLAG_UNSET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION); | ||
401 | 395 | } | ||
402 | 396 | |||
403 | 397 | (*G_OBJECT_CLASS (thunar_file_parent_class)->dispose) (object); | ||
404 | 398 | } | ||
405 | 399 | |||
406 | 400 | |||
407 | 401 | |||
408 | 402 | static void | ||
409 | 403 | thunar_file_finalize (GObject *object) | ||
410 | 404 | { | ||
411 | 405 | ThunarFile *file = THUNAR_FILE (object); | ||
412 | 406 | |||
413 | 407 | /* verify that nobody's watching the file anymore */ | ||
414 | 408 | #ifdef G_ENABLE_DEBUG | ||
415 | 409 | ThunarFileWatch *file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark); | ||
416 | 410 | if (file_watch != NULL) | ||
417 | 411 | { | ||
418 | 412 | g_error ("Attempt to finalize a ThunarFile, which has an active " | ||
419 | 413 | "watch count of %d", file_watch->watch_count); | ||
420 | 414 | } | ||
421 | 415 | #endif | ||
422 | 416 | |||
423 | 417 | /* drop the entry from the cache */ | ||
424 | 418 | G_LOCK (file_cache_mutex); | ||
425 | 419 | g_hash_table_remove (file_cache, file->gfile); | ||
426 | 420 | G_UNLOCK (file_cache_mutex); | ||
427 | 421 | |||
428 | 422 | /* release file info */ | ||
429 | 423 | if (file->info != NULL) | ||
430 | 424 | g_object_unref (file->info); | ||
431 | 425 | |||
432 | 426 | /* free the custom icon name */ | ||
433 | 427 | g_free (file->custom_icon_name); | ||
434 | 428 | |||
435 | 429 | /* content type info */ | ||
436 | 430 | g_free (file->content_type); | ||
437 | 431 | g_free (file->icon_name); | ||
438 | 432 | |||
439 | 433 | /* free display name and basename */ | ||
440 | 434 | g_free (file->display_name); | ||
441 | 435 | g_free (file->basename); | ||
442 | 436 | |||
443 | 437 | /* free collate keys */ | ||
444 | 438 | if (file->collate_key_nocase != file->collate_key) | ||
445 | 439 | g_free (file->collate_key_nocase); | ||
446 | 440 | g_free (file->collate_key); | ||
447 | 441 | |||
448 | 442 | /* free the thumbnail path */ | ||
449 | 443 | g_free (file->thumbnail_path); | ||
450 | 444 | |||
451 | 445 | /* release file */ | ||
452 | 446 | g_object_unref (file->gfile); | ||
453 | 447 | |||
454 | 448 | (*G_OBJECT_CLASS (thunar_file_parent_class)->finalize) (object); | ||
455 | 449 | } | ||
456 | 450 | |||
457 | 451 | |||
458 | 452 | |||
459 | 453 | static gchar * | ||
460 | 454 | thunar_file_info_get_name (ThunarxFileInfo *file_info) | ||
461 | 455 | { | ||
462 | 456 | return g_strdup (thunar_file_get_basename (THUNAR_FILE (file_info))); | ||
463 | 457 | } | ||
464 | 458 | |||
465 | 459 | |||
466 | 460 | |||
467 | 461 | static gchar* | ||
468 | 462 | thunar_file_info_get_uri (ThunarxFileInfo *file_info) | ||
469 | 463 | { | ||
470 | 464 | return thunar_file_dup_uri (THUNAR_FILE (file_info)); | ||
471 | 465 | } | ||
472 | 466 | |||
473 | 467 | |||
474 | 468 | |||
475 | 469 | static gchar* | ||
476 | 470 | thunar_file_info_get_parent_uri (ThunarxFileInfo *file_info) | ||
477 | 471 | { | ||
478 | 472 | GFile *parent; | ||
479 | 473 | gchar *uri = NULL; | ||
480 | 474 | |||
481 | 475 | parent = g_file_get_parent (THUNAR_FILE (file_info)->gfile); | ||
482 | 476 | if (G_LIKELY (parent != NULL)) | ||
483 | 477 | { | ||
484 | 478 | uri = g_file_get_uri (parent); | ||
485 | 479 | g_object_unref (parent); | ||
486 | 480 | } | ||
487 | 481 | |||
488 | 482 | return uri; | ||
489 | 483 | } | ||
490 | 484 | |||
491 | 485 | |||
492 | 486 | |||
493 | 487 | static gchar* | ||
494 | 488 | thunar_file_info_get_uri_scheme (ThunarxFileInfo *file_info) | ||
495 | 489 | { | ||
496 | 490 | return g_file_get_uri_scheme (THUNAR_FILE (file_info)->gfile); | ||
497 | 491 | } | ||
498 | 492 | |||
499 | 493 | |||
500 | 494 | |||
501 | 495 | static gchar* | ||
502 | 496 | thunar_file_info_get_mime_type (ThunarxFileInfo *file_info) | ||
503 | 497 | { | ||
504 | 498 | return g_strdup (thunar_file_get_content_type (THUNAR_FILE (file_info))); | ||
505 | 499 | } | ||
506 | 500 | |||
507 | 501 | |||
508 | 502 | |||
509 | 503 | static gboolean | ||
510 | 504 | thunar_file_info_has_mime_type (ThunarxFileInfo *file_info, | ||
511 | 505 | const gchar *mime_type) | ||
512 | 506 | { | ||
513 | 507 | if (THUNAR_FILE (file_info)->info == NULL) | ||
514 | 508 | return FALSE; | ||
515 | 509 | |||
516 | 510 | return g_content_type_is_a (thunar_file_get_content_type (THUNAR_FILE (file_info)), mime_type); | ||
517 | 511 | } | ||
518 | 512 | |||
519 | 513 | |||
520 | 514 | |||
521 | 515 | static gboolean | ||
522 | 516 | thunar_file_info_is_directory (ThunarxFileInfo *file_info) | ||
523 | 517 | { | ||
524 | 518 | return thunar_file_is_directory (THUNAR_FILE (file_info)); | ||
525 | 519 | } | ||
526 | 520 | |||
527 | 521 | |||
528 | 522 | |||
529 | 523 | static GFileInfo * | ||
530 | 524 | thunar_file_info_get_file_info (ThunarxFileInfo *file_info) | ||
531 | 525 | { | ||
532 | 526 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file_info), NULL); | ||
533 | 527 | |||
534 | 528 | if (THUNAR_FILE (file_info)->info != NULL) | ||
535 | 529 | return g_object_ref (THUNAR_FILE (file_info)->info); | ||
536 | 530 | else | ||
537 | 531 | return NULL; | ||
538 | 532 | } | ||
539 | 533 | |||
540 | 534 | |||
541 | 535 | |||
542 | 536 | static GFileInfo * | ||
543 | 537 | thunar_file_info_get_filesystem_info (ThunarxFileInfo *file_info) | ||
544 | 538 | { | ||
545 | 539 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file_info), NULL); | ||
546 | 540 | |||
547 | 541 | return g_file_query_filesystem_info (THUNAR_FILE (file_info)->gfile, | ||
548 | 542 | THUNARX_FILESYSTEM_INFO_NAMESPACE, | ||
549 | 543 | NULL, NULL); | ||
550 | 544 | } | ||
551 | 545 | |||
552 | 546 | |||
553 | 547 | |||
554 | 548 | static GFile * | ||
555 | 549 | thunar_file_info_get_location (ThunarxFileInfo *file_info) | ||
556 | 550 | { | ||
557 | 551 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file_info), NULL); | ||
558 | 552 | return g_object_ref (THUNAR_FILE (file_info)->gfile); | ||
559 | 553 | } | ||
560 | 554 | |||
561 | 555 | |||
562 | 556 | |||
563 | 557 | static void | ||
564 | 558 | thunar_file_info_changed (ThunarxFileInfo *file_info) | ||
565 | 559 | { | ||
566 | 560 | ThunarFile *file = THUNAR_FILE (file_info); | ||
567 | 561 | |||
568 | 562 | _thunar_return_if_fail (THUNAR_IS_FILE (file_info)); | ||
569 | 563 | |||
570 | 564 | /* set the new thumbnail state manually, so we only emit file | ||
571 | 565 | * changed once */ | ||
572 | 566 | FLAG_SET_THUMB_STATE (file, THUNAR_FILE_THUMB_STATE_UNKNOWN); | ||
573 | 567 | |||
574 | 568 | /* tell the file monitor that this file changed */ | ||
575 | 569 | thunar_file_monitor_file_changed (file); | ||
576 | 570 | } | ||
577 | 571 | |||
578 | 572 | |||
579 | 573 | |||
580 | 574 | static gboolean | ||
581 | 575 | thunar_file_denies_access_permission (const ThunarFile *file, | ||
582 | 576 | ThunarFileMode usr_permissions, | ||
583 | 577 | ThunarFileMode grp_permissions, | ||
584 | 578 | ThunarFileMode oth_permissions) | ||
585 | 579 | { | ||
586 | 580 | ThunarFileMode mode; | ||
587 | 581 | ThunarGroup *group; | ||
588 | 582 | ThunarUser *user; | ||
589 | 583 | gboolean result; | ||
590 | 584 | GList *groups; | ||
591 | 585 | GList *lp; | ||
592 | 586 | |||
593 | 587 | /* query the file mode */ | ||
594 | 588 | mode = thunar_file_get_mode (file); | ||
595 | 589 | |||
596 | 590 | /* query the owner of the file, if we cannot determine | ||
597 | 591 | * the owner, we can't tell if we're denied to access | ||
598 | 592 | * the file, so we simply return FALSE then. | ||
599 | 593 | */ | ||
600 | 594 | user = thunar_file_get_user (file); | ||
601 | 595 | if (G_UNLIKELY (user == NULL)) | ||
602 | 596 | return FALSE; | ||
603 | 597 | |||
604 | 598 | /* root is allowed to do everything */ | ||
605 | 599 | if (G_UNLIKELY (effective_user_id == 0)) | ||
606 | 600 | return FALSE; | ||
607 | 601 | |||
608 | 602 | if (thunar_user_is_me (user)) | ||
609 | 603 | { | ||
610 | 604 | /* we're the owner, so the usr permissions must be granted */ | ||
611 | 605 | result = ((mode & usr_permissions) == 0); | ||
612 | 606 | |||
613 | 607 | /* release the user */ | ||
614 | 608 | g_object_unref (G_OBJECT (user)); | ||
615 | 609 | } | ||
616 | 610 | else | ||
617 | 611 | { | ||
618 | 612 | group = thunar_file_get_group (file); | ||
619 | 613 | if (G_LIKELY (group != NULL)) | ||
620 | 614 | { | ||
621 | 615 | /* release the file owner */ | ||
622 | 616 | g_object_unref (G_OBJECT (user)); | ||
623 | 617 | |||
624 | 618 | /* determine the effective user */ | ||
625 | 619 | user = thunar_user_manager_get_user_by_id (user_manager, effective_user_id); | ||
626 | 620 | if (G_LIKELY (user != NULL)) | ||
627 | 621 | { | ||
628 | 622 | /* check the group permissions */ | ||
629 | 623 | groups = thunar_user_get_groups (user); | ||
630 | 624 | for (lp = groups; lp != NULL; lp = lp->next) | ||
631 | 625 | if (THUNAR_GROUP (lp->data) == group) | ||
632 | 626 | { | ||
633 | 627 | g_object_unref (G_OBJECT (user)); | ||
634 | 628 | g_object_unref (G_OBJECT (group)); | ||
635 | 629 | return ((mode & grp_permissions) == 0); | ||
636 | 630 | } | ||
637 | 631 | |||
638 | 632 | /* release the effective user */ | ||
639 | 633 | g_object_unref (G_OBJECT (user)); | ||
640 | 634 | } | ||
641 | 635 | |||
642 | 636 | /* release the file group */ | ||
643 | 637 | g_object_unref (G_OBJECT (group)); | ||
644 | 638 | } | ||
645 | 639 | |||
646 | 640 | /* check other permissions */ | ||
647 | 641 | result = ((mode & oth_permissions) == 0); | ||
648 | 642 | } | ||
649 | 643 | |||
650 | 644 | return result; | ||
651 | 645 | } | ||
652 | 646 | |||
653 | 647 | |||
654 | 648 | |||
655 | 649 | static void | ||
656 | 650 | thunar_file_monitor_update (GFile *path, | ||
657 | 651 | GFileMonitorEvent event_type) | ||
658 | 652 | { | ||
659 | 653 | ThunarFile *file; | ||
660 | 654 | |||
661 | 655 | _thunar_return_if_fail (G_IS_FILE (path)); | ||
662 | 656 | file = thunar_file_cache_lookup (path); | ||
663 | 657 | if (G_LIKELY (file != NULL)) | ||
664 | 658 | { | ||
665 | 659 | switch (event_type) | ||
666 | 660 | { | ||
667 | 661 | case G_FILE_MONITOR_EVENT_CREATED: | ||
668 | 662 | case G_FILE_MONITOR_EVENT_CHANGED: | ||
669 | 663 | case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: | ||
670 | 664 | thunar_file_reload (file); | ||
671 | 665 | break; | ||
672 | 666 | |||
673 | 667 | case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: | ||
674 | 668 | case G_FILE_MONITOR_EVENT_DELETED: | ||
675 | 669 | thunar_file_reload (file); | ||
676 | 670 | break; | ||
677 | 671 | |||
678 | 672 | default: | ||
679 | 673 | break; | ||
680 | 674 | } | ||
681 | 675 | } | ||
682 | 676 | } | ||
683 | 677 | |||
684 | 678 | |||
685 | 679 | |||
686 | 680 | static void | ||
687 | 681 | thunar_file_monitor (GFileMonitor *monitor, | ||
688 | 682 | GFile *path, | ||
689 | 683 | GFile *other_path, | ||
690 | 684 | GFileMonitorEvent event_type, | ||
691 | 685 | gpointer user_data) | ||
692 | 686 | { | ||
693 | 687 | ThunarFile *file = THUNAR_FILE (user_data); | ||
694 | 688 | |||
695 | 689 | _thunar_return_if_fail (G_IS_FILE_MONITOR (monitor)); | ||
696 | 690 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
697 | 691 | |||
698 | 692 | if (G_UNLIKELY (!G_IS_FILE (path) || !g_file_equal (path, file->gfile))) | ||
699 | 693 | return; | ||
700 | 694 | |||
701 | 695 | if (G_LIKELY (G_IS_FILE (path))) | ||
702 | 696 | thunar_file_monitor_update (path, event_type); | ||
703 | 697 | |||
704 | 698 | if (G_UNLIKELY (G_IS_FILE (other_path))) | ||
705 | 699 | thunar_file_monitor_update (other_path, event_type); | ||
706 | 700 | } | ||
707 | 701 | |||
708 | 702 | |||
709 | 703 | |||
710 | 704 | static void | ||
711 | 705 | thunar_file_watch_destroyed (gpointer data) | ||
712 | 706 | { | ||
713 | 707 | ThunarFileWatch *file_watch = data; | ||
714 | 708 | |||
715 | 709 | if (G_LIKELY (file_watch->monitor != NULL)) | ||
716 | 710 | { | ||
717 | 711 | g_file_monitor_cancel (file_watch->monitor); | ||
718 | 712 | g_object_unref (file_watch->monitor); | ||
719 | 713 | } | ||
720 | 714 | |||
721 | 715 | g_slice_free (ThunarFileWatch, file_watch); | ||
722 | 716 | } | ||
723 | 717 | |||
724 | 718 | |||
725 | 719 | |||
726 | 720 | static void | ||
727 | 721 | thunar_file_watch_reconnect (ThunarFile *file) | ||
728 | 722 | { | ||
729 | 723 | ThunarFileWatch *file_watch; | ||
730 | 724 | |||
731 | 725 | /* recreate the monitor without changing the watch_count for file renames */ | ||
732 | 726 | file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark); | ||
733 | 727 | if (file_watch != NULL) | ||
734 | 728 | { | ||
735 | 729 | /* reset the old monitor */ | ||
736 | 730 | if (G_LIKELY (file_watch->monitor != NULL)) | ||
737 | 731 | { | ||
738 | 732 | g_file_monitor_cancel (file_watch->monitor); | ||
739 | 733 | g_object_unref (file_watch->monitor); | ||
740 | 734 | } | ||
741 | 735 | |||
742 | 736 | /* create a file or directory monitor */ | ||
743 | 737 | file_watch->monitor = g_file_monitor (file->gfile, G_FILE_MONITOR_WATCH_MOUNTS, NULL, NULL); | ||
744 | 738 | if (G_LIKELY (file_watch->monitor != NULL)) | ||
745 | 739 | { | ||
746 | 740 | /* watch monitor for file changes */ | ||
747 | 741 | g_signal_connect (file_watch->monitor, "changed", G_CALLBACK (thunar_file_monitor), file); | ||
748 | 742 | } | ||
749 | 743 | } | ||
750 | 744 | } | ||
751 | 745 | |||
752 | 746 | |||
753 | 747 | |||
754 | 748 | static void | ||
755 | 749 | thunar_file_set_emblem_names_ready (GObject *source_object, | ||
756 | 750 | GAsyncResult *result, | ||
757 | 751 | gpointer user_data) | ||
758 | 752 | { | ||
759 | 753 | ThunarFile *file = THUNAR_FILE (user_data); | ||
760 | 754 | GError *error = NULL; | ||
761 | 755 | |||
762 | 756 | if (!g_file_set_attributes_finish (G_FILE (source_object), result, NULL, &error)) | ||
763 | 757 | { | ||
764 | 758 | g_warning ("Failed to set metadata: %s", error->message); | ||
765 | 759 | g_error_free (error); | ||
766 | 760 | |||
767 | 761 | g_file_info_remove_attribute (file->info, "metadata::emblems"); | ||
768 | 762 | } | ||
769 | 763 | |||
770 | 764 | thunar_file_changed (file); | ||
771 | 765 | } | ||
772 | 766 | |||
773 | 767 | |||
774 | 768 | |||
775 | 769 | static void | ||
776 | 770 | thunar_file_info_clear (ThunarFile *file) | ||
777 | 771 | { | ||
778 | 772 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
779 | 773 | |||
780 | 774 | /* release the current file info */ | ||
781 | 775 | if (file->info != NULL) | ||
782 | 776 | { | ||
783 | 777 | g_object_unref (file->info); | ||
784 | 778 | file->info = NULL; | ||
785 | 779 | } | ||
786 | 780 | |||
787 | 781 | /* unset */ | ||
788 | 782 | file->kind = G_FILE_TYPE_UNKNOWN; | ||
789 | 783 | |||
790 | 784 | /* free the custom icon name */ | ||
791 | 785 | g_free (file->custom_icon_name); | ||
792 | 786 | file->custom_icon_name = NULL; | ||
793 | 787 | |||
794 | 788 | /* free display name and basename */ | ||
795 | 789 | g_free (file->display_name); | ||
796 | 790 | file->display_name = NULL; | ||
797 | 791 | |||
798 | 792 | g_free (file->basename); | ||
799 | 793 | file->basename = NULL; | ||
800 | 794 | |||
801 | 795 | /* content type */ | ||
802 | 796 | g_free (file->content_type); | ||
803 | 797 | file->content_type = NULL; | ||
804 | 798 | g_free (file->icon_name); | ||
805 | 799 | file->icon_name = NULL; | ||
806 | 800 | |||
807 | 801 | /* free collate keys */ | ||
808 | 802 | if (file->collate_key_nocase != file->collate_key) | ||
809 | 803 | g_free (file->collate_key_nocase); | ||
810 | 804 | file->collate_key_nocase = NULL; | ||
811 | 805 | |||
812 | 806 | g_free (file->collate_key); | ||
813 | 807 | file->collate_key = NULL; | ||
814 | 808 | |||
815 | 809 | /* free thumbnail path */ | ||
816 | 810 | g_free (file->thumbnail_path); | ||
817 | 811 | file->thumbnail_path = NULL; | ||
818 | 812 | |||
819 | 813 | /* assume the file is mounted by default */ | ||
820 | 814 | FLAG_SET (file, THUNAR_FILE_FLAG_IS_MOUNTED); | ||
821 | 815 | |||
822 | 816 | /* set thumb state to unknown */ | ||
823 | 817 | FLAG_SET_THUMB_STATE (file, THUNAR_FILE_THUMB_STATE_UNKNOWN); | ||
824 | 818 | } | ||
825 | 819 | |||
826 | 820 | |||
827 | 821 | |||
828 | 822 | static void | ||
829 | 823 | thunar_file_info_reload (ThunarFile *file, | ||
830 | 824 | GCancellable *cancellable) | ||
831 | 825 | { | ||
832 | 826 | const gchar *target_uri; | ||
833 | 827 | GKeyFile *key_file; | ||
834 | 828 | gchar *p; | ||
835 | 829 | const gchar *display_name; | ||
836 | 830 | gboolean is_secure = FALSE; | ||
837 | 831 | gchar *casefold; | ||
838 | 832 | gchar *path; | ||
839 | 833 | |||
840 | 834 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
841 | 835 | _thunar_return_if_fail (file->info == NULL || G_IS_FILE_INFO (file->info)); | ||
842 | 836 | |||
843 | 837 | if (G_LIKELY (file->info != NULL)) | ||
844 | 838 | { | ||
845 | 839 | /* this is requesed so often, cache it */ | ||
846 | 840 | file->kind = g_file_info_get_file_type (file->info); | ||
847 | 841 | |||
848 | 842 | if (file->kind == G_FILE_TYPE_MOUNTABLE) | ||
849 | 843 | { | ||
850 | 844 | target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); | ||
851 | 845 | if (target_uri != NULL | ||
852 | 846 | && !g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT)) | ||
853 | 847 | FLAG_SET (file, THUNAR_FILE_FLAG_IS_MOUNTED); | ||
854 | 848 | else | ||
855 | 849 | FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED); | ||
856 | 850 | } | ||
857 | 851 | } | ||
858 | 852 | |||
859 | 853 | /* determine the basename */ | ||
860 | 854 | file->basename = g_file_get_basename (file->gfile); | ||
861 | 855 | _thunar_assert (file->basename != NULL); | ||
862 | 856 | |||
863 | 857 | /* problematic files with content type reading */ | ||
864 | 858 | if (strcmp (file->basename, "kmsg") == 0 | ||
865 | 859 | && g_file_is_native (file->gfile)) | ||
866 | 860 | { | ||
867 | 861 | path = g_file_get_path (file->gfile); | ||
868 | 862 | if (g_strcmp0 (path, "/proc/kmsg") == 0) | ||
869 | 863 | file->content_type = g_strdup (DEFAULT_CONTENT_TYPE); | ||
870 | 864 | g_free (path); | ||
871 | 865 | } | ||
872 | 866 | |||
873 | 867 | /* check if this file is a desktop entry */ | ||
874 | 868 | if (thunar_file_is_desktop_file (file, &is_secure) && is_secure) | ||
875 | 869 | { | ||
876 | 870 | /* determine the custom icon and display name for .desktop files */ | ||
877 | 871 | |||
878 | 872 | /* query a key file for the .desktop file */ | ||
879 | 873 | key_file = thunar_g_file_query_key_file (file->gfile, cancellable, NULL); | ||
880 | 874 | if (key_file != NULL) | ||
881 | 875 | { | ||
882 | 876 | /* read the icon name from the .desktop file */ | ||
883 | 877 | file->custom_icon_name = g_key_file_get_string (key_file, | ||
884 | 878 | G_KEY_FILE_DESKTOP_GROUP, | ||
885 | 879 | G_KEY_FILE_DESKTOP_KEY_ICON, | ||
886 | 880 | NULL); | ||
887 | 881 | |||
888 | 882 | if (G_UNLIKELY (exo_str_is_empty (file->custom_icon_name))) | ||
889 | 883 | { | ||
890 | 884 | /* make sure we set null if the string is empty else the assertion in | ||
891 | 885 | * thunar_icon_factory_lookup_icon() will fail */ | ||
892 | 886 | g_free (file->custom_icon_name); | ||
893 | 887 | file->custom_icon_name = NULL; | ||
894 | 888 | } | ||
895 | 889 | else | ||
896 | 890 | { | ||
897 | 891 | /* drop any suffix (e.g. '.png') from themed icons */ | ||
898 | 892 | if (!g_path_is_absolute (file->custom_icon_name)) | ||
899 | 893 | { | ||
900 | 894 | p = strrchr (file->custom_icon_name, '.'); | ||
901 | 895 | if (p != NULL) | ||
902 | 896 | *p = '\0'; | ||
903 | 897 | } | ||
904 | 898 | } | ||
905 | 899 | |||
906 | 900 | /* read the display name from the .desktop file (will be overwritten later | ||
907 | 901 | * if it's undefined here) */ | ||
908 | 902 | file->display_name = g_key_file_get_locale_string (key_file, | ||
909 | 903 | G_KEY_FILE_DESKTOP_GROUP, | ||
910 | 904 | G_KEY_FILE_DESKTOP_KEY_NAME, | ||
911 | 905 | NULL, NULL); | ||
912 | 906 | |||
913 | 907 | /* drop the name if it's empty or has invalid encoding */ | ||
914 | 908 | if (exo_str_is_empty (file->display_name) | ||
915 | 909 | || !g_utf8_validate (file->display_name, -1, NULL)) | ||
916 | 910 | { | ||
917 | 911 | g_free (file->display_name); | ||
918 | 912 | file->display_name = NULL; | ||
919 | 913 | } | ||
920 | 914 | |||
921 | 915 | /* free the key file */ | ||
922 | 916 | g_key_file_free (key_file); | ||
923 | 917 | } | ||
924 | 918 | } | ||
925 | 919 | |||
926 | 920 | /* determine the display name */ | ||
927 | 921 | if (file->display_name == NULL) | ||
928 | 922 | { | ||
929 | 923 | if (G_LIKELY (file->info != NULL)) | ||
930 | 924 | { | ||
931 | 925 | display_name = g_file_info_get_display_name (file->info); | ||
932 | 926 | if (G_LIKELY (display_name != NULL)) | ||
933 | 927 | { | ||
934 | 928 | if (strcmp (display_name, "/") == 0) | ||
935 | 929 | file->display_name = g_strdup (_("File System")); | ||
936 | 930 | else | ||
937 | 931 | file->display_name = g_strdup (display_name); | ||
938 | 932 | } | ||
939 | 933 | } | ||
940 | 934 | |||
941 | 935 | /* faccl back to a name for the gfile */ | ||
942 | 936 | if (file->display_name == NULL) | ||
943 | 937 | file->display_name = thunar_g_file_get_display_name (file->gfile); | ||
944 | 938 | } | ||
945 | 939 | |||
946 | 940 | /* create case sensitive collation key */ | ||
947 | 941 | file->collate_key = g_utf8_collate_key_for_filename (file->display_name, -1); | ||
948 | 942 | |||
949 | 943 | /* lowercase the display name */ | ||
950 | 944 | casefold = g_utf8_casefold (file->display_name, -1); | ||
951 | 945 | |||
952 | 946 | /* if the lowercase name is equal, only peek the already hash key */ | ||
953 | 947 | if (casefold != NULL && strcmp (casefold, file->display_name) != 0) | ||
954 | 948 | file->collate_key_nocase = g_utf8_collate_key_for_filename (casefold, -1); | ||
955 | 949 | else | ||
956 | 950 | file->collate_key_nocase = file->collate_key; | ||
957 | 951 | |||
958 | 952 | /* cleanup */ | ||
959 | 953 | g_free (casefold); | ||
960 | 954 | } | ||
961 | 955 | |||
962 | 956 | |||
963 | 957 | |||
964 | 958 | static void | ||
965 | 959 | thunar_file_get_async_finish (GObject *object, | ||
966 | 960 | GAsyncResult *result, | ||
967 | 961 | gpointer user_data) | ||
968 | 962 | { | ||
969 | 963 | ThunarFileGetData *data = user_data; | ||
970 | 964 | ThunarFile *file; | ||
971 | 965 | GFileInfo *file_info; | ||
972 | 966 | GError *error = NULL; | ||
973 | 967 | GFile *location = G_FILE (object); | ||
974 | 968 | |||
975 | 969 | _thunar_return_if_fail (G_IS_FILE (location)); | ||
976 | 970 | _thunar_return_if_fail (G_IS_ASYNC_RESULT (result)); | ||
977 | 971 | |||
978 | 972 | /* finish querying the file information */ | ||
979 | 973 | file_info = g_file_query_info_finish (location, result, &error); | ||
980 | 974 | |||
981 | 975 | /* allocate a new file object */ | ||
982 | 976 | file = g_object_new (THUNAR_TYPE_FILE, NULL); | ||
983 | 977 | file->gfile = g_object_ref (location); | ||
984 | 978 | |||
985 | 979 | /* reset the file */ | ||
986 | 980 | thunar_file_info_clear (file); | ||
987 | 981 | |||
988 | 982 | /* set the file information */ | ||
989 | 983 | file->info = file_info; | ||
990 | 984 | |||
991 | 985 | /* update the file from the information */ | ||
992 | 986 | thunar_file_info_reload (file, data->cancellable); | ||
993 | 987 | |||
994 | 988 | /* update the mounted info */ | ||
995 | 989 | if (error != NULL | ||
996 | 990 | && error->domain == G_IO_ERROR | ||
997 | 991 | && error->code == G_IO_ERROR_NOT_MOUNTED) | ||
998 | 992 | { | ||
999 | 993 | FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED); | ||
1000 | 994 | g_clear_error (&error); | ||
1001 | 995 | } | ||
1002 | 996 | |||
1003 | 997 | /* insert the file into the cache */ | ||
1004 | 998 | G_LOCK (file_cache_mutex); | ||
1005 | 999 | #ifdef G_ENABLE_DEBUG | ||
1006 | 1000 | /* check if there is no instance created in the meantime */ | ||
1007 | 1001 | _thunar_assert (g_hash_table_lookup (file_cache, file->gfile) == NULL); | ||
1008 | 1002 | #endif | ||
1009 | 1003 | g_hash_table_insert (file_cache, g_object_ref (file->gfile), file); | ||
1010 | 1004 | G_UNLOCK (file_cache_mutex); | ||
1011 | 1005 | |||
1012 | 1006 | /* pass the loaded file and possible errors to the return function */ | ||
1013 | 1007 | (data->func) (location, file, error, data->user_data); | ||
1014 | 1008 | |||
1015 | 1009 | /* release the file, see description in ThunarFileGetFunc */ | ||
1016 | 1010 | g_object_unref (file); | ||
1017 | 1011 | |||
1018 | 1012 | /* free the error, if there is any */ | ||
1019 | 1013 | if (error != NULL) | ||
1020 | 1014 | g_error_free (error); | ||
1021 | 1015 | |||
1022 | 1016 | /* release the get data */ | ||
1023 | 1017 | if (data->cancellable != NULL) | ||
1024 | 1018 | g_object_unref (data->cancellable); | ||
1025 | 1019 | g_slice_free (ThunarFileGetData, data); | ||
1026 | 1020 | } | ||
1027 | 1021 | |||
1028 | 1022 | |||
1029 | 1023 | |||
1030 | 1024 | /** | ||
1031 | 1025 | * thunar_file_load: | ||
1032 | 1026 | * @file : a #ThunarFile. | ||
1033 | 1027 | * @cancellable : a #GCancellable. | ||
1034 | 1028 | * @error : return location for errors or %NULL. | ||
1035 | 1029 | * | ||
1036 | 1030 | * Loads all information about the file. As this is a possibly | ||
1037 | 1031 | * blocking call, it can be cancelled using @cancellable. | ||
1038 | 1032 | * | ||
1039 | 1033 | * If loading the file fails or the operation is cancelled, | ||
1040 | 1034 | * @error will be set. | ||
1041 | 1035 | * | ||
1042 | 1036 | * Return value: %TRUE on success, %FALSE on error or interruption. | ||
1043 | 1037 | **/ | ||
1044 | 1038 | static gboolean | ||
1045 | 1039 | thunar_file_load (ThunarFile *file, | ||
1046 | 1040 | GCancellable *cancellable, | ||
1047 | 1041 | GError **error) | ||
1048 | 1042 | { | ||
1049 | 1043 | GError *err = NULL; | ||
1050 | 1044 | |||
1051 | 1045 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
1052 | 1046 | _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); | ||
1053 | 1047 | _thunar_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); | ||
1054 | 1048 | _thunar_return_val_if_fail (G_IS_FILE (file->gfile), FALSE); | ||
1055 | 1049 | |||
1056 | 1050 | /* reset the file */ | ||
1057 | 1051 | thunar_file_info_clear (file); | ||
1058 | 1052 | |||
1059 | 1053 | /* query a new file info */ | ||
1060 | 1054 | file->info = g_file_query_info (file->gfile, | ||
1061 | 1055 | THUNARX_FILE_INFO_NAMESPACE, | ||
1062 | 1056 | G_FILE_QUERY_INFO_NONE, | ||
1063 | 1057 | cancellable, &err); | ||
1064 | 1058 | |||
1065 | 1059 | /* update the file from the information */ | ||
1066 | 1060 | thunar_file_info_reload (file, cancellable); | ||
1067 | 1061 | |||
1068 | 1062 | /* update the mounted info */ | ||
1069 | 1063 | if (err != NULL | ||
1070 | 1064 | && err->domain == G_IO_ERROR | ||
1071 | 1065 | && err->code == G_IO_ERROR_NOT_MOUNTED) | ||
1072 | 1066 | { | ||
1073 | 1067 | FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED); | ||
1074 | 1068 | g_clear_error (&err); | ||
1075 | 1069 | } | ||
1076 | 1070 | |||
1077 | 1071 | if (err != NULL) | ||
1078 | 1072 | { | ||
1079 | 1073 | g_propagate_error (error, err); | ||
1080 | 1074 | return FALSE; | ||
1081 | 1075 | } | ||
1082 | 1076 | else | ||
1083 | 1077 | { | ||
1084 | 1078 | return TRUE; | ||
1085 | 1079 | } | ||
1086 | 1080 | } | ||
1087 | 1081 | |||
1088 | 1082 | |||
1089 | 1083 | |||
1090 | 1084 | /** | ||
1091 | 1085 | * thunar_file_get: | ||
1092 | 1086 | * @file : a #GFile. | ||
1093 | 1087 | * @error : return location for errors. | ||
1094 | 1088 | * | ||
1095 | 1089 | * Looks up the #ThunarFile referred to by @file. This function may return a | ||
1096 | 1090 | * ThunarFile even though the file doesn't actually exist. This is the case | ||
1097 | 1091 | * with remote URIs (like SFTP) for instance, if they are not mounted. | ||
1098 | 1092 | * | ||
1099 | 1093 | * The caller is responsible to call g_object_unref() | ||
1100 | 1094 | * when done with the returned object. | ||
1101 | 1095 | * | ||
1102 | 1096 | * Return value: the #ThunarFile for @file or %NULL on errors. | ||
1103 | 1097 | **/ | ||
1104 | 1098 | ThunarFile* | ||
1105 | 1099 | thunar_file_get (GFile *gfile, | ||
1106 | 1100 | GError **error) | ||
1107 | 1101 | { | ||
1108 | 1102 | ThunarFile *file; | ||
1109 | 1103 | |||
1110 | 1104 | _thunar_return_val_if_fail (G_IS_FILE (gfile), NULL); | ||
1111 | 1105 | |||
1112 | 1106 | /* check if we already have a cached version of that file */ | ||
1113 | 1107 | file = thunar_file_cache_lookup (gfile); | ||
1114 | 1108 | if (G_UNLIKELY (file != NULL)) | ||
1115 | 1109 | { | ||
1116 | 1110 | /* take a reference for the caller */ | ||
1117 | 1111 | g_object_ref (file); | ||
1118 | 1112 | } | ||
1119 | 1113 | else | ||
1120 | 1114 | { | ||
1121 | 1115 | /* allocate a new object */ | ||
1122 | 1116 | file = g_object_new (THUNAR_TYPE_FILE, NULL); | ||
1123 | 1117 | file->gfile = g_object_ref (gfile); | ||
1124 | 1118 | |||
1125 | 1119 | if (thunar_file_load (file, NULL, error)) | ||
1126 | 1120 | { | ||
1127 | 1121 | /* setup lock until the file is inserted */ | ||
1128 | 1122 | G_LOCK (file_cache_mutex); | ||
1129 | 1123 | |||
1130 | 1124 | /* insert the file into the cache */ | ||
1131 | 1125 | g_hash_table_insert (file_cache, g_object_ref (file->gfile), file); | ||
1132 | 1126 | |||
1133 | 1127 | /* done inserting in the cache */ | ||
1134 | 1128 | G_UNLOCK (file_cache_mutex); | ||
1135 | 1129 | } | ||
1136 | 1130 | else | ||
1137 | 1131 | { | ||
1138 | 1132 | /* failed loading, destroy the file */ | ||
1139 | 1133 | g_object_unref (file); | ||
1140 | 1134 | |||
1141 | 1135 | /* make sure we return NULL */ | ||
1142 | 1136 | file = NULL; | ||
1143 | 1137 | } | ||
1144 | 1138 | } | ||
1145 | 1139 | |||
1146 | 1140 | return file; | ||
1147 | 1141 | } | ||
1148 | 1142 | |||
1149 | 1143 | |||
1150 | 1144 | /** | ||
1151 | 1145 | * thunar_file_get_with_info: | ||
1152 | 1146 | * @uri : an URI or an absolute filename. | ||
1153 | 1147 | * @info : #GFileInfo to use when loading the info. | ||
1154 | 1148 | * @not_mounted : if the file is mounted. | ||
1155 | 1149 | * | ||
1156 | 1150 | * Looks up the #ThunarFile referred to by @file. This function may return a | ||
1157 | 1151 | * ThunarFile even though the file doesn't actually exist. This is the case | ||
1158 | 1152 | * with remote URIs (like SFTP) for instance, if they are not mounted. | ||
1159 | 1153 | * | ||
1160 | 1154 | * This function does not use g_file_query_info() to get the info, | ||
1161 | 1155 | * but takes a reference on the @info, | ||
1162 | 1156 | * | ||
1163 | 1157 | * The caller is responsible to call g_object_unref() | ||
1164 | 1158 | * when done with the returned object. | ||
1165 | 1159 | * | ||
1166 | 1160 | * Return value: the #ThunarFile for @file or %NULL on errors. | ||
1167 | 1161 | **/ | ||
1168 | 1162 | ThunarFile * | ||
1169 | 1163 | thunar_file_get_with_info (GFile *gfile, | ||
1170 | 1164 | GFileInfo *info, | ||
1171 | 1165 | gboolean not_mounted) | ||
1172 | 1166 | { | ||
1173 | 1167 | ThunarFile *file; | ||
1174 | 1168 | |||
1175 | 1169 | _thunar_return_val_if_fail (G_IS_FILE (gfile), NULL); | ||
1176 | 1170 | _thunar_return_val_if_fail (G_IS_FILE_INFO (info), NULL); | ||
1177 | 1171 | |||
1178 | 1172 | /* check if we already have a cached version of that file */ | ||
1179 | 1173 | file = thunar_file_cache_lookup (gfile); | ||
1180 | 1174 | if (G_UNLIKELY (file != NULL)) | ||
1181 | 1175 | { | ||
1182 | 1176 | /* take a reference for the caller */ | ||
1183 | 1177 | g_object_ref (file); | ||
1184 | 1178 | } | ||
1185 | 1179 | else | ||
1186 | 1180 | { | ||
1187 | 1181 | /* allocate a new object */ | ||
1188 | 1182 | file = g_object_new (THUNAR_TYPE_FILE, NULL); | ||
1189 | 1183 | file->gfile = g_object_ref (gfile); | ||
1190 | 1184 | |||
1191 | 1185 | /* reset the file */ | ||
1192 | 1186 | thunar_file_info_clear (file); | ||
1193 | 1187 | |||
1194 | 1188 | /* set the passed info */ | ||
1195 | 1189 | file->info = g_object_ref (info); | ||
1196 | 1190 | |||
1197 | 1191 | /* update the file from the information */ | ||
1198 | 1192 | thunar_file_info_reload (file, NULL); | ||
1199 | 1193 | |||
1200 | 1194 | /* update the mounted info */ | ||
1201 | 1195 | if (not_mounted) | ||
1202 | 1196 | FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED); | ||
1203 | 1197 | |||
1204 | 1198 | /* setup lock until the file is inserted */ | ||
1205 | 1199 | G_LOCK (file_cache_mutex); | ||
1206 | 1200 | |||
1207 | 1201 | /* insert the file into the cache */ | ||
1208 | 1202 | g_hash_table_insert (file_cache, g_object_ref (file->gfile), file); | ||
1209 | 1203 | |||
1210 | 1204 | /* done inserting in the cache */ | ||
1211 | 1205 | G_UNLOCK (file_cache_mutex); | ||
1212 | 1206 | } | ||
1213 | 1207 | |||
1214 | 1208 | return file; | ||
1215 | 1209 | } | ||
1216 | 1210 | |||
1217 | 1211 | |||
1218 | 1212 | |||
1219 | 1213 | |||
1220 | 1214 | |||
1221 | 1215 | /** | ||
1222 | 1216 | * thunar_file_get_for_uri: | ||
1223 | 1217 | * @uri : an URI or an absolute filename. | ||
1224 | 1218 | * @error : return location for errors or %NULL. | ||
1225 | 1219 | * | ||
1226 | 1220 | * Convenience wrapper function for thunar_file_get_for_path(), as its | ||
1227 | 1221 | * often required to determine a #ThunarFile for a given @uri. | ||
1228 | 1222 | * | ||
1229 | 1223 | * The caller is responsible to free the returned object using | ||
1230 | 1224 | * g_object_unref() when no longer needed. | ||
1231 | 1225 | * | ||
1232 | 1226 | * Return value: the #ThunarFile for the given @uri or %NULL if | ||
1233 | 1227 | * unable to determine. | ||
1234 | 1228 | **/ | ||
1235 | 1229 | ThunarFile* | ||
1236 | 1230 | thunar_file_get_for_uri (const gchar *uri, | ||
1237 | 1231 | GError **error) | ||
1238 | 1232 | { | ||
1239 | 1233 | ThunarFile *file; | ||
1240 | 1234 | GFile *path; | ||
1241 | 1235 | |||
1242 | 1236 | _thunar_return_val_if_fail (uri != NULL, NULL); | ||
1243 | 1237 | _thunar_return_val_if_fail (error == NULL || *error == NULL, NULL); | ||
1244 | 1238 | |||
1245 | 1239 | path = g_file_new_for_commandline_arg (uri); | ||
1246 | 1240 | file = thunar_file_get (path, error); | ||
1247 | 1241 | g_object_unref (path); | ||
1248 | 1242 | |||
1249 | 1243 | return file; | ||
1250 | 1244 | } | ||
1251 | 1245 | |||
1252 | 1246 | |||
1253 | 1247 | |||
1254 | 1248 | /** | ||
1255 | 1249 | * thunar_file_get_async: | ||
1256 | 1250 | **/ | ||
1257 | 1251 | void | ||
1258 | 1252 | thunar_file_get_async (GFile *location, | ||
1259 | 1253 | GCancellable *cancellable, | ||
1260 | 1254 | ThunarFileGetFunc func, | ||
1261 | 1255 | gpointer user_data) | ||
1262 | 1256 | { | ||
1263 | 1257 | ThunarFile *file; | ||
1264 | 1258 | ThunarFileGetData *data; | ||
1265 | 1259 | |||
1266 | 1260 | _thunar_return_if_fail (G_IS_FILE (location)); | ||
1267 | 1261 | _thunar_return_if_fail (func != NULL); | ||
1268 | 1262 | |||
1269 | 1263 | /* check if we already have a cached version of that file */ | ||
1270 | 1264 | file = thunar_file_cache_lookup (location); | ||
1271 | 1265 | if (G_UNLIKELY (file != NULL)) | ||
1272 | 1266 | { | ||
1273 | 1267 | /* call the return function with the file from the cache */ | ||
1274 | 1268 | (func) (location, file, NULL, user_data); | ||
1275 | 1269 | } | ||
1276 | 1270 | else | ||
1277 | 1271 | { | ||
1278 | 1272 | /* allocate get data */ | ||
1279 | 1273 | data = g_slice_new0 (ThunarFileGetData); | ||
1280 | 1274 | data->user_data = user_data; | ||
1281 | 1275 | data->func = func; | ||
1282 | 1276 | if (cancellable != NULL) | ||
1283 | 1277 | data->cancellable = g_object_ref (cancellable); | ||
1284 | 1278 | |||
1285 | 1279 | /* load the file information asynchronously */ | ||
1286 | 1280 | g_file_query_info_async (location, | ||
1287 | 1281 | THUNARX_FILE_INFO_NAMESPACE, | ||
1288 | 1282 | G_FILE_QUERY_INFO_NONE, | ||
1289 | 1283 | G_PRIORITY_DEFAULT, | ||
1290 | 1284 | cancellable, | ||
1291 | 1285 | thunar_file_get_async_finish, | ||
1292 | 1286 | data); | ||
1293 | 1287 | } | ||
1294 | 1288 | } | ||
1295 | 1289 | |||
1296 | 1290 | |||
1297 | 1291 | |||
1298 | 1292 | /** | ||
1299 | 1293 | * thunar_file_get_file: | ||
1300 | 1294 | * @file : a #ThunarFile instance. | ||
1301 | 1295 | * | ||
1302 | 1296 | * Returns the #GFile that refers to the location of @file. | ||
1303 | 1297 | * | ||
1304 | 1298 | * The returned #GFile is owned by @file and must not be released | ||
1305 | 1299 | * with g_object_unref(). | ||
1306 | 1300 | * | ||
1307 | 1301 | * Return value: the #GFile corresponding to @file. | ||
1308 | 1302 | **/ | ||
1309 | 1303 | GFile * | ||
1310 | 1304 | thunar_file_get_file (const ThunarFile *file) | ||
1311 | 1305 | { | ||
1312 | 1306 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
1313 | 1307 | _thunar_return_val_if_fail (G_IS_FILE (file->gfile), NULL); | ||
1314 | 1308 | return file->gfile; | ||
1315 | 1309 | } | ||
1316 | 1310 | |||
1317 | 1311 | |||
1318 | 1312 | |||
1319 | 1313 | /** | ||
1320 | 1314 | * thunar_file_get_info: | ||
1321 | 1315 | * @file : a #ThunarFile instance. | ||
1322 | 1316 | * | ||
1323 | 1317 | * Returns the #GFileInfo for @file. | ||
1324 | 1318 | * | ||
1325 | 1319 | * Note, that there's no reference taken for the caller on the | ||
1326 | 1320 | * returned #GFileInfo, so if you need the object for a longer | ||
1327 | 1321 | * perioud, you'll need to take a reference yourself using the | ||
1328 | 1322 | * g_object_ref() method. | ||
1329 | 1323 | * | ||
1330 | 1324 | * Return value: the #GFileInfo for @file or %NULL. | ||
1331 | 1325 | **/ | ||
1332 | 1326 | GFileInfo * | ||
1333 | 1327 | thunar_file_get_info (const ThunarFile *file) | ||
1334 | 1328 | { | ||
1335 | 1329 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
1336 | 1330 | _thunar_return_val_if_fail (file->info == NULL || G_IS_FILE_INFO (file->info), NULL); | ||
1337 | 1331 | return file->info; | ||
1338 | 1332 | } | ||
1339 | 1333 | |||
1340 | 1334 | |||
1341 | 1335 | |||
1342 | 1336 | /** | ||
1343 | 1337 | * thunar_file_get_parent: | ||
1344 | 1338 | * @file : a #ThunarFile instance. | ||
1345 | 1339 | * @error : return location for errors. | ||
1346 | 1340 | * | ||
1347 | 1341 | * Determines the parent #ThunarFile for @file. If @file has no parent or | ||
1348 | 1342 | * the user is not allowed to open the parent folder of @file, %NULL will | ||
1349 | 1343 | * be returned and @error will be set to point to a #GError that | ||
1350 | 1344 | * describes the cause. Else, the #ThunarFile will be returned, and | ||
1351 | 1345 | * the caller must call g_object_unref() on it. | ||
1352 | 1346 | * | ||
1353 | 1347 | * You may want to call thunar_file_has_parent() first to | ||
1354 | 1348 | * determine whether @file has a parent. | ||
1355 | 1349 | * | ||
1356 | 1350 | * Return value: the parent #ThunarFile or %NULL. | ||
1357 | 1351 | **/ | ||
1358 | 1352 | ThunarFile* | ||
1359 | 1353 | thunar_file_get_parent (const ThunarFile *file, | ||
1360 | 1354 | GError **error) | ||
1361 | 1355 | { | ||
1362 | 1356 | ThunarFile *parent = NULL; | ||
1363 | 1357 | GFile *parent_file; | ||
1364 | 1358 | |||
1365 | 1359 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
1366 | 1360 | _thunar_return_val_if_fail (error == NULL || *error == NULL, NULL); | ||
1367 | 1361 | |||
1368 | 1362 | parent_file = g_file_get_parent (file->gfile); | ||
1369 | 1363 | |||
1370 | 1364 | if (parent_file == NULL) | ||
1371 | 1365 | { | ||
1372 | 1366 | g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, _("The root folder has no parent")); | ||
1373 | 1367 | return NULL; | ||
1374 | 1368 | } | ||
1375 | 1369 | |||
1376 | 1370 | parent = thunar_file_get (parent_file, error); | ||
1377 | 1371 | g_object_unref (parent_file); | ||
1378 | 1372 | |||
1379 | 1373 | return parent; | ||
1380 | 1374 | } | ||
1381 | 1375 | |||
1382 | 1376 | |||
1383 | 1377 | |||
1384 | 1378 | /** | ||
1385 | 1379 | * thunar_file_check_loaded: | ||
1386 | 1380 | * @file : a #ThunarFile instance. | ||
1387 | 1381 | * | ||
1388 | 1382 | * Check if @file has its information loaded, if not, try this once else | ||
1389 | 1383 | * return %FALSE. | ||
1390 | 1384 | * | ||
1391 | 1385 | * Return value: %TRUE on success, else %FALSE. | ||
1392 | 1386 | **/ | ||
1393 | 1387 | gboolean | ||
1394 | 1388 | thunar_file_check_loaded (ThunarFile *file) | ||
1395 | 1389 | { | ||
1396 | 1390 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
1397 | 1391 | |||
1398 | 1392 | if (G_UNLIKELY (file->info == NULL)) | ||
1399 | 1393 | thunar_file_load (file, NULL, NULL); | ||
1400 | 1394 | |||
1401 | 1395 | return (file->info != NULL); | ||
1402 | 1396 | } | ||
1403 | 1397 | |||
1404 | 1398 | |||
1405 | 1399 | |||
1406 | 1400 | /** | ||
1407 | 1401 | * thunar_file_execute: | ||
1408 | 1402 | * @file : a #ThunarFile instance. | ||
1409 | 1403 | * @working_directory : the working directory used to resolve relative filenames | ||
1410 | 1404 | * in @file_list. | ||
1411 | 1405 | * @parent : %NULL, a #GdkScreen or #GtkWidget. | ||
1412 | 1406 | * @file_list : the list of #GFile<!---->s to supply to @file on execution. | ||
1413 | 1407 | * @error : return location for errors or %NULL. | ||
1414 | 1408 | * | ||
1415 | 1409 | * Tries to execute @file on the specified @screen. If @file is executable | ||
1416 | 1410 | * and could have been spawned successfully, %TRUE is returned, else %FALSE | ||
1417 | 1411 | * will be returned and @error will be set to point to the error location. | ||
1418 | 1412 | * | ||
1419 | 1413 | * Return value: %TRUE on success, else %FALSE. | ||
1420 | 1414 | **/ | ||
1421 | 1415 | gboolean | ||
1422 | 1416 | thunar_file_execute (ThunarFile *file, | ||
1423 | 1417 | GFile *working_directory, | ||
1424 | 1418 | gpointer parent, | ||
1425 | 1419 | GList *file_list, | ||
1426 | 1420 | GError **error) | ||
1427 | 1421 | { | ||
1428 | 1422 | gboolean snotify = FALSE; | ||
1429 | 1423 | gboolean terminal; | ||
1430 | 1424 | gboolean result = FALSE; | ||
1431 | 1425 | GKeyFile *key_file; | ||
1432 | 1426 | GError *err = NULL; | ||
1433 | 1427 | GFile *file_parent; | ||
1434 | 1428 | gchar *icon_name = NULL; | ||
1435 | 1429 | gchar *name; | ||
1436 | 1430 | gchar *type; | ||
1437 | 1431 | gchar *url; | ||
1438 | 1432 | gchar *location; | ||
1439 | 1433 | gchar *escaped_location; | ||
1440 | 1434 | gchar **argv = NULL; | ||
1441 | 1435 | gchar *exec; | ||
1442 | 1436 | gchar *directory = NULL; | ||
1443 | 1437 | gboolean is_secure = FALSE; | ||
1444 | 1438 | |||
1445 | 1439 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
1446 | 1440 | _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); | ||
1447 | 1441 | |||
1448 | 1442 | location = thunar_g_file_get_location (file->gfile); | ||
1449 | 1443 | |||
1450 | 1444 | if (thunar_file_is_desktop_file (file, &is_secure)) | ||
1451 | 1445 | { | ||
1452 | 1446 | /* parse file first, even if it is insecure */ | ||
1453 | 1447 | key_file = thunar_g_file_query_key_file (file->gfile, NULL, &err); | ||
1454 | 1448 | if (key_file == NULL) | ||
1455 | 1449 | { | ||
1456 | 1450 | g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, | ||
1457 | 1451 | _("Failed to parse the desktop file: %s"), err->message); | ||
1458 | 1452 | g_error_free (err); | ||
1459 | 1453 | return FALSE; | ||
1460 | 1454 | } | ||
1461 | 1455 | |||
1462 | 1456 | type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TYPE, NULL); | ||
1463 | 1457 | if (G_LIKELY (exo_str_is_equal (type, "Application"))) | ||
1464 | 1458 | { | ||
1465 | 1459 | exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL); | ||
1466 | 1460 | if (G_LIKELY (exec != NULL)) | ||
1467 | 1461 | { | ||
1468 | 1462 | /* if the .desktop file is not secure, ask user what to do */ | ||
1469 | 1463 | if (is_secure || thunar_dialogs_show_insecure_program (parent, _("Untrusted application launcher"), file, exec)) | ||
1470 | 1464 | { | ||
1471 | 1465 | /* parse other fields */ | ||
1472 | 1466 | name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL); | ||
1473 | 1467 | icon_name = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL); | ||
1474 | 1468 | directory = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL); | ||
1475 | 1469 | terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL); | ||
1476 | 1470 | snotify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL); | ||
1477 | 1471 | |||
1478 | 1472 | result = thunar_exec_parse (exec, file_list, icon_name, name, location, terminal, NULL, &argv, error); | ||
1479 | 1473 | |||
1480 | 1474 | g_free (name); | ||
1481 | 1475 | } | ||
1482 | 1476 | else | ||
1483 | 1477 | { | ||
1484 | 1478 | /* fall-through to free value and leave without execution */ | ||
1485 | 1479 | result = TRUE; | ||
1486 | 1480 | } | ||
1487 | 1481 | |||
1488 | 1482 | g_free (exec); | ||
1489 | 1483 | } | ||
1490 | 1484 | else | ||
1491 | 1485 | { | ||
1492 | 1486 | /* TRANSLATORS: `Exec' is a field name in a .desktop file. Don't translate it. */ | ||
1493 | 1487 | g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, | ||
1494 | 1488 | _("No Exec field specified")); | ||
1495 | 1489 | } | ||
1496 | 1490 | } | ||
1497 | 1491 | else if (exo_str_is_equal (type, "Link")) | ||
1498 | 1492 | { | ||
1499 | 1493 | url = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, NULL); | ||
1500 | 1494 | if (G_LIKELY (url != NULL)) | ||
1501 | 1495 | { | ||
1502 | 1496 | /* if the .desktop file is not secure, ask user what to do */ | ||
1503 | 1497 | if (is_secure || thunar_dialogs_show_insecure_program (parent, _("Untrusted link launcher"), file, url)) | ||
1504 | 1498 | { | ||
1505 | 1499 | /* pass the URL to the webbrowser, this could be a bit strange, | ||
1506 | 1500 | * but then at least we are on the secure side */ | ||
1507 | 1501 | argv = g_new (gchar *, 3); | ||
1508 | 1502 | argv[0] = g_strdup ("exo-open"); | ||
1509 | 1503 | argv[1] = url; | ||
1510 | 1504 | argv[2] = NULL; | ||
1511 | 1505 | } | ||
1512 | 1506 | |||
1513 | 1507 | result = TRUE; | ||
1514 | 1508 | } | ||
1515 | 1509 | else | ||
1516 | 1510 | { | ||
1517 | 1511 | /* TRANSLATORS: `URL' is a field name in a .desktop file. Don't translate it. */ | ||
1518 | 1512 | g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, | ||
1519 | 1513 | _("No URL field specified")); | ||
1520 | 1514 | } | ||
1521 | 1515 | } | ||
1522 | 1516 | else | ||
1523 | 1517 | { | ||
1524 | 1518 | g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, _("Invalid desktop file")); | ||
1525 | 1519 | } | ||
1526 | 1520 | |||
1527 | 1521 | g_free (type); | ||
1528 | 1522 | g_key_file_free (key_file); | ||
1529 | 1523 | } | ||
1530 | 1524 | else | ||
1531 | 1525 | { | ||
1532 | 1526 | /* fake the Exec line */ | ||
1533 | 1527 | escaped_location = g_shell_quote (location); | ||
1534 | 1528 | exec = g_strconcat (escaped_location, " %F", NULL); | ||
1535 | 1529 | result = thunar_exec_parse (exec, file_list, NULL, NULL, NULL, FALSE, NULL, &argv, error); | ||
1536 | 1530 | g_free (escaped_location); | ||
1537 | 1531 | g_free (exec); | ||
1538 | 1532 | } | ||
1539 | 1533 | |||
1540 | 1534 | if (G_LIKELY (result && argv != NULL)) | ||
1541 | 1535 | { | ||
1542 | 1536 | /* use other directory if the Path from the desktop file was not set */ | ||
1543 | 1537 | if (G_LIKELY (directory == NULL)) | ||
1544 | 1538 | { | ||
1545 | 1539 | /* determine the working directory */ | ||
1546 | 1540 | if (G_LIKELY (working_directory != NULL)) | ||
1547 | 1541 | { | ||
1548 | 1542 | /* copy the working directory provided to this method */ | ||
1549 | 1543 | directory = g_file_get_path (working_directory); | ||
1550 | 1544 | } | ||
1551 | 1545 | else if (file_list != NULL) | ||
1552 | 1546 | { | ||
1553 | 1547 | /* use the directory of the first list item */ | ||
1554 | 1548 | file_parent = g_file_get_parent (file_list->data); | ||
1555 | 1549 | directory = (file_parent != NULL) ? thunar_g_file_get_location (file_parent) : NULL; | ||
1556 | 1550 | g_object_unref (file_parent); | ||
1557 | 1551 | } | ||
1558 | 1552 | else | ||
1559 | 1553 | { | ||
1560 | 1554 | /* use the directory of the executable file */ | ||
1561 | 1555 | parent = g_file_get_parent (file->gfile); | ||
1562 | 1556 | directory = (parent != NULL) ? thunar_g_file_get_location (parent) : NULL; | ||
1563 | 1557 | g_object_unref (parent); | ||
1564 | 1558 | } | ||
1565 | 1559 | } | ||
1566 | 1560 | |||
1567 | 1561 | /* execute the command */ | ||
1568 | 1562 | result = xfce_spawn_on_screen (thunar_util_parse_parent (parent, NULL), | ||
1569 | 1563 | directory, argv, NULL, G_SPAWN_SEARCH_PATH, | ||
1570 | 1564 | snotify, gtk_get_current_event_time (), icon_name, error); | ||
1571 | 1565 | } | ||
1572 | 1566 | |||
1573 | 1567 | /* clean up */ | ||
1574 | 1568 | g_strfreev (argv); | ||
1575 | 1569 | g_free (location); | ||
1576 | 1570 | g_free (directory); | ||
1577 | 1571 | g_free (icon_name); | ||
1578 | 1572 | |||
1579 | 1573 | return result; | ||
1580 | 1574 | } | ||
1581 | 1575 | |||
1582 | 1576 | |||
1583 | 1577 | |||
1584 | 1578 | /** | ||
1585 | 1579 | * thunar_file_launch: | ||
1586 | 1580 | * @file : a #ThunarFile instance. | ||
1587 | 1581 | * @parent : a #GtkWidget or a #GdkScreen on which to launch the @file. | ||
1588 | 1582 | * May also be %NULL in which case the default #GdkScreen will | ||
1589 | 1583 | * be used. | ||
1590 | 1584 | * @startup_id : startup id for the new window (send over for dbus) or %NULL. | ||
1591 | 1585 | * @error : return location for errors or %NULL. | ||
1592 | 1586 | * | ||
1593 | 1587 | * If @file is an executable file, tries to execute it. Else if @file is | ||
1594 | 1588 | * a directory, opens a new #ThunarWindow to display the directory. Else, | ||
1595 | 1589 | * the default handler for @file is determined and run. | ||
1596 | 1590 | * | ||
1597 | 1591 | * The @parent can be either a #GtkWidget or a #GdkScreen, on which to | ||
1598 | 1592 | * launch the @file. If @parent is a #GtkWidget, the chooser dialog (if | ||
1599 | 1593 | * no default application is available for @file) will be transient for | ||
1600 | 1594 | * @parent. Else if @parent is a #GdkScreen it specifies the screen on | ||
1601 | 1595 | * which to launch @file. | ||
1602 | 1596 | * | ||
1603 | 1597 | * Return value: %TRUE on success, else %FALSE. | ||
1604 | 1598 | **/ | ||
1605 | 1599 | gboolean | ||
1606 | 1600 | thunar_file_launch (ThunarFile *file, | ||
1607 | 1601 | gpointer parent, | ||
1608 | 1602 | const gchar *startup_id, | ||
1609 | 1603 | GError **error) | ||
1610 | 1604 | { | ||
1611 | 1605 | GdkAppLaunchContext *context; | ||
1612 | 1606 | ThunarApplication *application; | ||
1613 | 1607 | GAppInfo *app_info; | ||
1614 | 1608 | gboolean succeed; | ||
1615 | 1609 | GList path_list; | ||
1616 | 1610 | GdkScreen *screen; | ||
1617 | 1611 | |||
1618 | 1612 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
1619 | 1613 | _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); | ||
1620 | 1614 | _thunar_return_val_if_fail (parent == NULL || GDK_IS_SCREEN (parent) || GTK_IS_WIDGET (parent), FALSE); | ||
1621 | 1615 | |||
1622 | 1616 | screen = thunar_util_parse_parent (parent, NULL); | ||
1623 | 1617 | |||
1624 | 1618 | /* check if we have a folder here */ | ||
1625 | 1619 | if (thunar_file_is_directory (file)) | ||
1626 | 1620 | { | ||
1627 | 1621 | application = thunar_application_get (); | ||
1628 | 1622 | thunar_application_open_window (application, file, screen, startup_id); | ||
1629 | 1623 | g_object_unref (G_OBJECT (application)); | ||
1630 | 1624 | return TRUE; | ||
1631 | 1625 | } | ||
1632 | 1626 | |||
1633 | 1627 | /* check if we should execute the file */ | ||
1634 | 1628 | if (thunar_file_is_executable (file)) | ||
1635 | 1629 | return thunar_file_execute (file, NULL, parent, NULL, error); | ||
1636 | 1630 | |||
1637 | 1631 | /* determine the default application to open the file */ | ||
1638 | 1632 | /* TODO We should probably add a cancellable argument to thunar_file_launch() */ | ||
1639 | 1633 | app_info = thunar_file_get_default_handler (THUNAR_FILE (file)); | ||
1640 | 1634 | |||
1641 | 1635 | /* display the application chooser if no application is defined for this file | ||
1642 | 1636 | * type yet */ | ||
1643 | 1637 | if (G_UNLIKELY (app_info == NULL)) | ||
1644 | 1638 | { | ||
1645 | 1639 | thunar_show_chooser_dialog (parent, file, TRUE); | ||
1646 | 1640 | return TRUE; | ||
1647 | 1641 | } | ||
1648 | 1642 | |||
1649 | 1643 | /* HACK: check if we're not trying to launch another file manager again, possibly | ||
1650 | 1644 | * ourselfs which will end in a loop */ | ||
1651 | 1645 | if (g_strcmp0 (g_app_info_get_id (app_info), "exo-file-manager.desktop") == 0 | ||
1652 | 1646 | || g_strcmp0 (g_app_info_get_id (app_info), "Thunar.desktop") == 0 | ||
1653 | 1647 | || g_strcmp0 (g_app_info_get_name (app_info), "exo-file-manager") == 0) | ||
1654 | 1648 | { | ||
1655 | 1649 | g_object_unref (G_OBJECT (app_info)); | ||
1656 | 1650 | thunar_show_chooser_dialog (parent, file, TRUE); | ||
1657 | 1651 | return TRUE; | ||
1658 | 1652 | } | ||
1659 | 1653 | |||
1660 | 1654 | /* fake a path list */ | ||
1661 | 1655 | path_list.data = file->gfile; | ||
1662 | 1656 | path_list.next = path_list.prev = NULL; | ||
1663 | 1657 | |||
1664 | 1658 | /* create a launch context */ | ||
1665 | 1659 | context = gdk_app_launch_context_new (); | ||
1666 | 1660 | gdk_app_launch_context_set_screen (context, screen); | ||
1667 | 1661 | gdk_app_launch_context_set_timestamp (context, gtk_get_current_event_time ()); | ||
1668 | 1662 | |||
1669 | 1663 | /* otherwise try to execute the application */ | ||
1670 | 1664 | succeed = g_app_info_launch (app_info, &path_list, G_APP_LAUNCH_CONTEXT (context), error); | ||
1671 | 1665 | |||
1672 | 1666 | /* destroy the launch context */ | ||
1673 | 1667 | g_object_unref (context); | ||
1674 | 1668 | |||
1675 | 1669 | /* release the handler reference */ | ||
1676 | 1670 | g_object_unref (G_OBJECT (app_info)); | ||
1677 | 1671 | |||
1678 | 1672 | return succeed; | ||
1679 | 1673 | } | ||
1680 | 1674 | |||
1681 | 1675 | |||
1682 | 1676 | |||
1683 | 1677 | /** | ||
1684 | 1678 | * thunar_file_rename: | ||
1685 | 1679 | * @file : a #ThunarFile instance. | ||
1686 | 1680 | * @name : the new file name in UTF-8 encoding. | ||
1687 | 1681 | * @error : return location for errors or %NULL. | ||
1688 | 1682 | * | ||
1689 | 1683 | * Tries to rename @file to the new @name. If @file cannot be renamed, | ||
1690 | 1684 | * %FALSE will be returned and @error will be set accordingly. Else, if | ||
1691 | 1685 | * the operation succeeds, %TRUE will be returned, and @file will have | ||
1692 | 1686 | * a new URI and a new display name. | ||
1693 | 1687 | * | ||
1694 | 1688 | * When offering a rename action in the user interface, the implementation | ||
1695 | 1689 | * should first check whether the file is available, using the | ||
1696 | 1690 | * thunar_file_is_renameable() method. | ||
1697 | 1691 | * | ||
1698 | 1692 | * Return value: %TRUE on success, else %FALSE. | ||
1699 | 1693 | **/ | ||
1700 | 1694 | gboolean | ||
1701 | 1695 | thunar_file_rename (ThunarFile *file, | ||
1702 | 1696 | const gchar *name, | ||
1703 | 1697 | GCancellable *cancellable, | ||
1704 | 1698 | gboolean called_from_job, | ||
1705 | 1699 | GError **error) | ||
1706 | 1700 | { | ||
1707 | 1701 | ThunarApplication *application; | ||
1708 | 1702 | ThunarThumbnailCache *thumbnail_cache; | ||
1709 | 1703 | GKeyFile *key_file; | ||
1710 | 1704 | GError *err = NULL; | ||
1711 | 1705 | GFile *previous_file; | ||
1712 | 1706 | GFile *renamed_file; | ||
1713 | 1707 | gboolean is_secure; | ||
1714 | 1708 | const gchar * const *languages; | ||
1715 | 1709 | guint i; | ||
1716 | 1710 | gboolean name_set = FALSE; | ||
1717 | 1711 | |||
1718 | 1712 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
1719 | 1713 | _thunar_return_val_if_fail (g_utf8_validate (name, -1, NULL), FALSE); | ||
1720 | 1714 | _thunar_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); | ||
1721 | 1715 | _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); | ||
1722 | 1716 | |||
1723 | 1717 | /* check if this file is a desktop entry */ | ||
1724 | 1718 | if (thunar_file_is_desktop_file (file, &is_secure) | ||
1725 | 1719 | && is_secure) | ||
1726 | 1720 | { | ||
1727 | 1721 | /* try to load the desktop entry into a key file */ | ||
1728 | 1722 | key_file = thunar_g_file_query_key_file (file->gfile, cancellable, &err); | ||
1729 | 1723 | if (key_file == NULL) | ||
1730 | 1724 | { | ||
1731 | 1725 | g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, | ||
1732 | 1726 | _("Failed to parse the desktop file: %s"), err->message); | ||
1733 | 1727 | g_error_free (err); | ||
1734 | 1728 | return FALSE; | ||
1735 | 1729 | } | ||
1736 | 1730 | |||
1737 | 1731 | /* check if we can set the language name */ | ||
1738 | 1732 | languages = g_get_language_names (); | ||
1739 | 1733 | if (languages != NULL) | ||
1740 | 1734 | { | ||
1741 | 1735 | for (i = 0; !name_set && languages[i] != NULL; i++) | ||
1742 | 1736 | { | ||
1743 | 1737 | /* skip C language */ | ||
1744 | 1738 | if (g_ascii_strcasecmp (languages[i], "C") == 0) | ||
1745 | 1739 | continue; | ||
1746 | 1740 | |||
1747 | 1741 | /* change the translated Name field of the desktop entry */ | ||
1748 | 1742 | g_key_file_set_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, | ||
1749 | 1743 | G_KEY_FILE_DESKTOP_KEY_NAME, | ||
1750 | 1744 | languages[i], name); | ||
1751 | 1745 | |||
1752 | 1746 | /* done */ | ||
1753 | 1747 | name_set = TRUE; | ||
1754 | 1748 | } | ||
1755 | 1749 | } | ||
1756 | 1750 | |||
1757 | 1751 | if (!name_set) | ||
1758 | 1752 | { | ||
1759 | 1753 | /* change the Name field of the desktop entry */ | ||
1760 | 1754 | g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, | ||
1761 | 1755 | G_KEY_FILE_DESKTOP_KEY_NAME, name); | ||
1762 | 1756 | } | ||
1763 | 1757 | |||
1764 | 1758 | /* write the changes back to the file */ | ||
1765 | 1759 | if (thunar_g_file_write_key_file (file->gfile, key_file, cancellable, &err)) | ||
1766 | 1760 | { | ||
1767 | 1761 | /* reload file information */ | ||
1768 | 1762 | thunar_file_load (file, NULL, NULL); | ||
1769 | 1763 | |||
1770 | 1764 | if (!called_from_job) | ||
1771 | 1765 | { | ||
1772 | 1766 | /* tell the associated folder that the file was renamed */ | ||
1773 | 1767 | thunarx_file_info_renamed (THUNARX_FILE_INFO (file)); | ||
1774 | 1768 | |||
1775 | 1769 | /* notify everybody that the file has changed */ | ||
1776 | 1770 | thunar_file_changed (file); | ||
1777 | 1771 | } | ||
1778 | 1772 | |||
1779 | 1773 | /* release the key file and return with success */ | ||
1780 | 1774 | g_key_file_free (key_file); | ||
1781 | 1775 | return TRUE; | ||
1782 | 1776 | } | ||
1783 | 1777 | else | ||
1784 | 1778 | { | ||
1785 | 1779 | /* propagate the error message and return with failure */ | ||
1786 | 1780 | g_propagate_error (error, err); | ||
1787 | 1781 | g_key_file_free (key_file); | ||
1788 | 1782 | return FALSE; | ||
1789 | 1783 | } | ||
1790 | 1784 | } | ||
1791 | 1785 | else | ||
1792 | 1786 | { | ||
1793 | 1787 | /* remember the previous file */ | ||
1794 | 1788 | previous_file = g_object_ref (file->gfile); | ||
1795 | 1789 | |||
1796 | 1790 | /* try to rename the file */ | ||
1797 | 1791 | renamed_file = g_file_set_display_name (file->gfile, name, cancellable, error); | ||
1798 | 1792 | |||
1799 | 1793 | /* notify the thumbnail cache that we can now also move the thumbnail */ | ||
1800 | 1794 | application = thunar_application_get (); | ||
1801 | 1795 | thumbnail_cache = thunar_application_get_thumbnail_cache (application); | ||
1802 | 1796 | thunar_thumbnail_cache_move_file (thumbnail_cache, previous_file, renamed_file); | ||
1803 | 1797 | g_object_unref (thumbnail_cache); | ||
1804 | 1798 | g_object_unref (application); | ||
1805 | 1799 | |||
1806 | 1800 | /* check if we succeeded */ | ||
1807 | 1801 | if (renamed_file != NULL) | ||
1808 | 1802 | { | ||
1809 | 1803 | /* set the new file */ | ||
1810 | 1804 | file->gfile = renamed_file; | ||
1811 | 1805 | |||
1812 | 1806 | /* reload file information */ | ||
1813 | 1807 | thunar_file_load (file, NULL, NULL); | ||
1814 | 1808 | |||
1815 | 1809 | /* need to re-register the monitor handle for the new uri */ | ||
1816 | 1810 | thunar_file_watch_reconnect (file); | ||
1817 | 1811 | |||
1818 | 1812 | G_LOCK (file_cache_mutex); | ||
1819 | 1813 | |||
1820 | 1814 | /* drop the previous entry from the cache */ | ||
1821 | 1815 | g_hash_table_remove (file_cache, previous_file); | ||
1822 | 1816 | |||
1823 | 1817 | /* drop the reference on the previous file */ | ||
1824 | 1818 | g_object_unref (previous_file); | ||
1825 | 1819 | |||
1826 | 1820 | /* insert the new entry */ | ||
1827 | 1821 | g_hash_table_insert (file_cache, g_object_ref (file->gfile), file); | ||
1828 | 1822 | |||
1829 | 1823 | G_UNLOCK (file_cache_mutex); | ||
1830 | 1824 | |||
1831 | 1825 | if (!called_from_job) | ||
1832 | 1826 | { | ||
1833 | 1827 | /* tell the associated folder that the file was renamed */ | ||
1834 | 1828 | thunarx_file_info_renamed (THUNARX_FILE_INFO (file)); | ||
1835 | 1829 | |||
1836 | 1830 | /* emit the file changed signal */ | ||
1837 | 1831 | thunar_file_changed (file); | ||
1838 | 1832 | } | ||
1839 | 1833 | |||
1840 | 1834 | return TRUE; | ||
1841 | 1835 | } | ||
1842 | 1836 | else | ||
1843 | 1837 | { | ||
1844 | 1838 | g_object_unref (previous_file); | ||
1845 | 1839 | |||
1846 | 1840 | return FALSE; | ||
1847 | 1841 | } | ||
1848 | 1842 | } | ||
1849 | 1843 | } | ||
1850 | 1844 | |||
1851 | 1845 | |||
1852 | 1846 | |||
1853 | 1847 | /** | ||
1854 | 1848 | * thunar_file_accepts_drop: | ||
1855 | 1849 | * @file : a #ThunarFile instance. | ||
1856 | 1850 | * @file_list : the list of #GFile<!---->s that will be droppped. | ||
1857 | 1851 | * @context : the current #GdkDragContext, which is used for the drop. | ||
1858 | 1852 | * @suggested_action_return : return location for the suggested #GdkDragAction or %NULL. | ||
1859 | 1853 | * | ||
1860 | 1854 | * Checks whether @file can accept @path_list for the given @context and | ||
1861 | 1855 | * returns the #GdkDragAction<!---->s that can be used or 0 if no actions | ||
1862 | 1856 | * apply. | ||
1863 | 1857 | * | ||
1864 | 1858 | * If any #GdkDragAction<!---->s apply and @suggested_action_return is not | ||
1865 | 1859 | * %NULL, the suggested #GdkDragAction for this drop will be stored to the | ||
1866 | 1860 | * location pointed to by @suggested_action_return. | ||
1867 | 1861 | * | ||
1868 | 1862 | * Return value: the #GdkDragAction<!---->s supported for the drop or | ||
1869 | 1863 | * 0 if no drop is possible. | ||
1870 | 1864 | **/ | ||
1871 | 1865 | GdkDragAction | ||
1872 | 1866 | thunar_file_accepts_drop (ThunarFile *file, | ||
1873 | 1867 | GList *file_list, | ||
1874 | 1868 | GdkDragContext *context, | ||
1875 | 1869 | GdkDragAction *suggested_action_return) | ||
1876 | 1870 | { | ||
1877 | 1871 | GdkDragAction suggested_action; | ||
1878 | 1872 | GdkDragAction actions; | ||
1879 | 1873 | ThunarFile *ofile; | ||
1880 | 1874 | GFile *parent_file; | ||
1881 | 1875 | GList *lp; | ||
1882 | 1876 | guint n; | ||
1883 | 1877 | |||
1884 | 1878 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0); | ||
1885 | 1879 | _thunar_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), 0); | ||
1886 | 1880 | |||
1887 | 1881 | /* we can never drop an empty list */ | ||
1888 | 1882 | if (G_UNLIKELY (file_list == NULL)) | ||
1889 | 1883 | return 0; | ||
1890 | 1884 | |||
1891 | 1885 | /* default to whatever GTK+ thinks for the suggested action */ | ||
1892 | 1886 | suggested_action = context->suggested_action; | ||
1893 | 1887 | |||
1894 | 1888 | /* check if we have a writable directory here or an executable file */ | ||
1895 | 1889 | if (thunar_file_is_directory (file) && thunar_file_is_writable (file)) | ||
1896 | 1890 | { | ||
1897 | 1891 | /* determine the possible actions */ | ||
1898 | 1892 | actions = context->actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK); | ||
1899 | 1893 | |||
1900 | 1894 | /* cannot create symbolic links in the trash or copy to the trash */ | ||
1901 | 1895 | if (thunar_file_is_trashed (file)) | ||
1902 | 1896 | actions &= ~(GDK_ACTION_COPY | GDK_ACTION_LINK); | ||
1903 | 1897 | |||
1904 | 1898 | /* check up to 100 of the paths (just in case somebody tries to | ||
1905 | 1899 | * drag around his music collection with 5000 files). | ||
1906 | 1900 | */ | ||
1907 | 1901 | for (lp = file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n) | ||
1908 | 1902 | { | ||
1909 | 1903 | /* we cannot drop a file on itself */ | ||
1910 | 1904 | if (G_UNLIKELY (g_file_equal (file->gfile, lp->data))) | ||
1911 | 1905 | return 0; | ||
1912 | 1906 | |||
1913 | 1907 | /* check whether source and destination are the same */ | ||
1914 | 1908 | parent_file = g_file_get_parent (lp->data); | ||
1915 | 1909 | if (G_LIKELY (parent_file != NULL)) | ||
1916 | 1910 | { | ||
1917 | 1911 | if (g_file_equal (file->gfile, parent_file)) | ||
1918 | 1912 | { | ||
1919 | 1913 | g_object_unref (parent_file); | ||
1920 | 1914 | return 0; | ||
1921 | 1915 | } | ||
1922 | 1916 | else | ||
1923 | 1917 | g_object_unref (parent_file); | ||
1924 | 1918 | } | ||
1925 | 1919 | |||
1926 | 1920 | /* copy/move/link within the trash not possible */ | ||
1927 | 1921 | if (G_UNLIKELY (thunar_g_file_is_trashed (lp->data) && thunar_file_is_trashed (file))) | ||
1928 | 1922 | return 0; | ||
1929 | 1923 | } | ||
1930 | 1924 | |||
1931 | 1925 | /* if the source offers both copy and move and the GTK+ suggested action is copy, try to be smart telling whether | ||
1932 | 1926 | * we should copy or move by default by checking whether the source and target are on the same disk. | ||
1933 | 1927 | */ | ||
1934 | 1928 | if ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE)) != 0 | ||
1935 | 1929 | && (suggested_action == GDK_ACTION_COPY)) | ||
1936 | 1930 | { | ||
1937 | 1931 | /* default to move as suggested action */ | ||
1938 | 1932 | suggested_action = GDK_ACTION_MOVE; | ||
1939 | 1933 | |||
1940 | 1934 | /* check for up to 100 files, for the reason state above */ | ||
1941 | 1935 | for (lp = file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n) | ||
1942 | 1936 | { | ||
1943 | 1937 | /* dropping from the trash always suggests move */ | ||
1944 | 1938 | if (G_UNLIKELY (thunar_g_file_is_trashed (lp->data))) | ||
1945 | 1939 | break; | ||
1946 | 1940 | |||
1947 | 1941 | /* determine the cached version of the source file */ | ||
1948 | 1942 | ofile = thunar_file_cache_lookup (lp->data); | ||
1949 | 1943 | |||
1950 | 1944 | /* we have only move if we know the source and both the source and the target | ||
1951 | 1945 | * are on the same disk, and the source file is owned by the current user. | ||
1952 | 1946 | */ | ||
1953 | 1947 | if (ofile == NULL | ||
1954 | 1948 | || !thunar_file_same_filesystem (file, ofile) | ||
1955 | 1949 | || (ofile->info != NULL | ||
1956 | 1950 | && g_file_info_get_attribute_uint32 (ofile->info, | ||
1957 | 1951 | G_FILE_ATTRIBUTE_UNIX_UID) != effective_user_id)) | ||
1958 | 1952 | { | ||
1959 | 1953 | /* default to copy and get outa here */ | ||
1960 | 1954 | suggested_action = GDK_ACTION_COPY; | ||
1961 | 1955 | break; | ||
1962 | 1956 | } | ||
1963 | 1957 | } | ||
1964 | 1958 | } | ||
1965 | 1959 | } | ||
1966 | 1960 | else if (thunar_file_is_executable (file)) | ||
1967 | 1961 | { | ||
1968 | 1962 | /* determine the possible actions */ | ||
1969 | 1963 | actions = context->actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE); | ||
1970 | 1964 | } | ||
1971 | 1965 | else | ||
1972 | 1966 | return 0; | ||
1973 | 1967 | |||
1974 | 1968 | /* determine the preferred action based on the context */ | ||
1975 | 1969 | if (G_LIKELY (suggested_action_return != NULL)) | ||
1976 | 1970 | { | ||
1977 | 1971 | /* determine a working action */ | ||
1978 | 1972 | if (G_LIKELY ((suggested_action & actions) != 0)) | ||
1979 | 1973 | *suggested_action_return = suggested_action; | ||
1980 | 1974 | else if ((actions & GDK_ACTION_ASK) != 0) | ||
1981 | 1975 | *suggested_action_return = GDK_ACTION_ASK; | ||
1982 | 1976 | else if ((actions & GDK_ACTION_COPY) != 0) | ||
1983 | 1977 | *suggested_action_return = GDK_ACTION_COPY; | ||
1984 | 1978 | else if ((actions & GDK_ACTION_LINK) != 0) | ||
1985 | 1979 | *suggested_action_return = GDK_ACTION_LINK; | ||
1986 | 1980 | else if ((actions & GDK_ACTION_MOVE) != 0) | ||
1987 | 1981 | *suggested_action_return = GDK_ACTION_MOVE; | ||
1988 | 1982 | else | ||
1989 | 1983 | *suggested_action_return = GDK_ACTION_PRIVATE; | ||
1990 | 1984 | } | ||
1991 | 1985 | |||
1992 | 1986 | /* yeppa, we can drop here */ | ||
1993 | 1987 | return actions; | ||
1994 | 1988 | } | ||
1995 | 1989 | |||
1996 | 1990 | |||
1997 | 1991 | |||
1998 | 1992 | /** | ||
1999 | 1993 | * thunar_file_get_date: | ||
2000 | 1994 | * @file : a #ThunarFile instance. | ||
2001 | 1995 | * @date_type : the kind of date you are interested in. | ||
2002 | 1996 | * | ||
2003 | 1997 | * Queries the given @date_type from @file and returns the result. | ||
2004 | 1998 | * | ||
2005 | 1999 | * Return value: the time for @file of the given @date_type. | ||
2006 | 2000 | **/ | ||
2007 | 2001 | guint64 | ||
2008 | 2002 | thunar_file_get_date (const ThunarFile *file, | ||
2009 | 2003 | ThunarFileDateType date_type) | ||
2010 | 2004 | { | ||
2011 | 2005 | const gchar *attribute; | ||
2012 | 2006 | |||
2013 | 2007 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0); | ||
2014 | 2008 | |||
2015 | 2009 | if (file->info == NULL) | ||
2016 | 2010 | return 0; | ||
2017 | 2011 | |||
2018 | 2012 | switch (date_type) | ||
2019 | 2013 | { | ||
2020 | 2014 | case THUNAR_FILE_DATE_ACCESSED: | ||
2021 | 2015 | attribute = G_FILE_ATTRIBUTE_TIME_ACCESS; | ||
2022 | 2016 | break; | ||
2023 | 2017 | case THUNAR_FILE_DATE_CHANGED: | ||
2024 | 2018 | attribute = G_FILE_ATTRIBUTE_TIME_CHANGED; | ||
2025 | 2019 | break; | ||
2026 | 2020 | case THUNAR_FILE_DATE_MODIFIED: | ||
2027 | 2021 | attribute = G_FILE_ATTRIBUTE_TIME_MODIFIED; | ||
2028 | 2022 | break; | ||
2029 | 2023 | default: | ||
2030 | 2024 | _thunar_assert_not_reached (); | ||
2031 | 2025 | } | ||
2032 | 2026 | |||
2033 | 2027 | return g_file_info_get_attribute_uint64 (file->info, attribute); | ||
2034 | 2028 | } | ||
2035 | 2029 | |||
2036 | 2030 | |||
2037 | 2031 | |||
2038 | 2032 | /** | ||
2039 | 2033 | * thunar_file_get_date_string: | ||
2040 | 2034 | * @file : a #ThunarFile instance. | ||
2041 | 2035 | * @date_type : the kind of date you are interested to know about @file. | ||
2042 | 2036 | * @date_style : the style used to format the date. | ||
2043 | 2037 | * | ||
2044 | 2038 | * Tries to determine the @date_type of @file, and if @file supports the | ||
2045 | 2039 | * given @date_type, it'll be formatted as string and returned. The | ||
2046 | 2040 | * caller is responsible for freeing the string using the g_free() | ||
2047 | 2041 | * function. | ||
2048 | 2042 | * | ||
2049 | 2043 | * Return value: the @date_type of @file formatted as string. | ||
2050 | 2044 | **/ | ||
2051 | 2045 | gchar* | ||
2052 | 2046 | thunar_file_get_date_string (const ThunarFile *file, | ||
2053 | 2047 | ThunarFileDateType date_type, | ||
2054 | 2048 | ThunarDateStyle date_style) | ||
2055 | 2049 | { | ||
2056 | 2050 | return thunar_util_humanize_file_time (thunar_file_get_date (file, date_type), date_style); | ||
2057 | 2051 | } | ||
2058 | 2052 | |||
2059 | 2053 | |||
2060 | 2054 | |||
2061 | 2055 | /** | ||
2062 | 2056 | * thunar_file_get_mode_string: | ||
2063 | 2057 | * @file : a #ThunarFile instance. | ||
2064 | 2058 | * | ||
2065 | 2059 | * Returns the mode of @file as text. You'll need to free | ||
2066 | 2060 | * the result using g_free() when you're done with it. | ||
2067 | 2061 | * | ||
2068 | 2062 | * Return value: the mode of @file as string. | ||
2069 | 2063 | **/ | ||
2070 | 2064 | gchar* | ||
2071 | 2065 | thunar_file_get_mode_string (const ThunarFile *file) | ||
2072 | 2066 | { | ||
2073 | 2067 | ThunarFileMode mode; | ||
2074 | 2068 | GFileType kind; | ||
2075 | 2069 | gchar *text; | ||
2076 | 2070 | |||
2077 | 2071 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2078 | 2072 | |||
2079 | 2073 | kind = thunar_file_get_kind (file); | ||
2080 | 2074 | mode = thunar_file_get_mode (file); | ||
2081 | 2075 | text = g_new (gchar, 11); | ||
2082 | 2076 | |||
2083 | 2077 | /* file type */ | ||
2084 | 2078 | /* TODO earlier versions of Thunar had 'P' for ports and | ||
2085 | 2079 | * 'D' for doors. Do we still need those? */ | ||
2086 | 2080 | switch (kind) | ||
2087 | 2081 | { | ||
2088 | 2082 | case G_FILE_TYPE_SYMBOLIC_LINK: text[0] = 'l'; break; | ||
2089 | 2083 | case G_FILE_TYPE_REGULAR: text[0] = '-'; break; | ||
2090 | 2084 | case G_FILE_TYPE_DIRECTORY: text[0] = 'd'; break; | ||
2091 | 2085 | case G_FILE_TYPE_SPECIAL: | ||
2092 | 2086 | case G_FILE_TYPE_UNKNOWN: | ||
2093 | 2087 | default: | ||
2094 | 2088 | if (S_ISCHR (mode)) | ||
2095 | 2089 | text[0] = 'c'; | ||
2096 | 2090 | else if (S_ISSOCK (mode)) | ||
2097 | 2091 | text[0] = 's'; | ||
2098 | 2092 | else if (S_ISFIFO (mode)) | ||
2099 | 2093 | text[0] = 'f'; | ||
2100 | 2094 | else if (S_ISBLK (mode)) | ||
2101 | 2095 | text[0] = 'b'; | ||
2102 | 2096 | else | ||
2103 | 2097 | text[0] = ' '; | ||
2104 | 2098 | } | ||
2105 | 2099 | |||
2106 | 2100 | /* permission flags */ | ||
2107 | 2101 | text[1] = (mode & THUNAR_FILE_MODE_USR_READ) ? 'r' : '-'; | ||
2108 | 2102 | text[2] = (mode & THUNAR_FILE_MODE_USR_WRITE) ? 'w' : '-'; | ||
2109 | 2103 | text[3] = (mode & THUNAR_FILE_MODE_USR_EXEC) ? 'x' : '-'; | ||
2110 | 2104 | text[4] = (mode & THUNAR_FILE_MODE_GRP_READ) ? 'r' : '-'; | ||
2111 | 2105 | text[5] = (mode & THUNAR_FILE_MODE_GRP_WRITE) ? 'w' : '-'; | ||
2112 | 2106 | text[6] = (mode & THUNAR_FILE_MODE_GRP_EXEC) ? 'x' : '-'; | ||
2113 | 2107 | text[7] = (mode & THUNAR_FILE_MODE_OTH_READ) ? 'r' : '-'; | ||
2114 | 2108 | text[8] = (mode & THUNAR_FILE_MODE_OTH_WRITE) ? 'w' : '-'; | ||
2115 | 2109 | text[9] = (mode & THUNAR_FILE_MODE_OTH_EXEC) ? 'x' : '-'; | ||
2116 | 2110 | |||
2117 | 2111 | /* special flags */ | ||
2118 | 2112 | if (G_UNLIKELY (mode & THUNAR_FILE_MODE_SUID)) | ||
2119 | 2113 | text[3] = 's'; | ||
2120 | 2114 | if (G_UNLIKELY (mode & THUNAR_FILE_MODE_SGID)) | ||
2121 | 2115 | text[6] = 's'; | ||
2122 | 2116 | if (G_UNLIKELY (mode & THUNAR_FILE_MODE_STICKY)) | ||
2123 | 2117 | text[9] = 't'; | ||
2124 | 2118 | |||
2125 | 2119 | text[10] = '\0'; | ||
2126 | 2120 | |||
2127 | 2121 | return text; | ||
2128 | 2122 | } | ||
2129 | 2123 | |||
2130 | 2124 | |||
2131 | 2125 | |||
2132 | 2126 | /** | ||
2133 | 2127 | * thunar_file_get_size_string: | ||
2134 | 2128 | * @file : a #ThunarFile instance. | ||
2135 | 2129 | * | ||
2136 | 2130 | * Returns the size of the file as text in a human readable | ||
2137 | 2131 | * format. You'll need to free the result using g_free() | ||
2138 | 2132 | * if you're done with it. | ||
2139 | 2133 | * | ||
2140 | 2134 | * Return value: the size of @file in a human readable | ||
2141 | 2135 | * format. | ||
2142 | 2136 | **/ | ||
2143 | 2137 | gchar * | ||
2144 | 2138 | thunar_file_get_size_string (const ThunarFile *file) | ||
2145 | 2139 | { | ||
2146 | 2140 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2147 | 2141 | return g_format_size (thunar_file_get_size (file)); | ||
2148 | 2142 | } | ||
2149 | 2143 | |||
2150 | 2144 | |||
2151 | 2145 | |||
2152 | 2146 | /** | ||
2153 | 2147 | * thunar_file_get_volume: | ||
2154 | 2148 | * @file : a #ThunarFile instance. | ||
2155 | 2149 | * | ||
2156 | 2150 | * Attempts to determine the #GVolume on which @file is located. If @file cannot | ||
2157 | 2151 | * determine it's volume, then %NULL will be returned. Else a #GVolume instance | ||
2158 | 2152 | * is returned which has to be released by the caller using g_object_unref(). | ||
2159 | 2153 | * | ||
2160 | 2154 | * Return value: the #GVolume for @file or %NULL. | ||
2161 | 2155 | **/ | ||
2162 | 2156 | GVolume* | ||
2163 | 2157 | thunar_file_get_volume (const ThunarFile *file) | ||
2164 | 2158 | { | ||
2165 | 2159 | GVolume *volume = NULL; | ||
2166 | 2160 | GMount *mount; | ||
2167 | 2161 | |||
2168 | 2162 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2169 | 2163 | |||
2170 | 2164 | /* TODO make this function call asynchronous */ | ||
2171 | 2165 | mount = g_file_find_enclosing_mount (file->gfile, NULL, NULL); | ||
2172 | 2166 | if (mount != NULL) | ||
2173 | 2167 | { | ||
2174 | 2168 | volume = g_mount_get_volume (mount); | ||
2175 | 2169 | g_object_unref (mount); | ||
2176 | 2170 | } | ||
2177 | 2171 | |||
2178 | 2172 | return volume; | ||
2179 | 2173 | } | ||
2180 | 2174 | |||
2181 | 2175 | |||
2182 | 2176 | |||
2183 | 2177 | /** | ||
2184 | 2178 | * thunar_file_get_group: | ||
2185 | 2179 | * @file : a #ThunarFile instance. | ||
2186 | 2180 | * | ||
2187 | 2181 | * Determines the #ThunarGroup for @file. If there's no | ||
2188 | 2182 | * group associated with @file or if the system is unable to | ||
2189 | 2183 | * determine the group, %NULL will be returned. | ||
2190 | 2184 | * | ||
2191 | 2185 | * The caller is responsible for freeing the returned object | ||
2192 | 2186 | * using g_object_unref(). | ||
2193 | 2187 | * | ||
2194 | 2188 | * Return value: the #ThunarGroup for @file or %NULL. | ||
2195 | 2189 | **/ | ||
2196 | 2190 | ThunarGroup * | ||
2197 | 2191 | thunar_file_get_group (const ThunarFile *file) | ||
2198 | 2192 | { | ||
2199 | 2193 | guint32 gid; | ||
2200 | 2194 | |||
2201 | 2195 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2202 | 2196 | |||
2203 | 2197 | /* TODO what are we going to do on non-UNIX systems? */ | ||
2204 | 2198 | gid = g_file_info_get_attribute_uint32 (file->info, | ||
2205 | 2199 | G_FILE_ATTRIBUTE_UNIX_GID); | ||
2206 | 2200 | |||
2207 | 2201 | return thunar_user_manager_get_group_by_id (user_manager, gid); | ||
2208 | 2202 | } | ||
2209 | 2203 | |||
2210 | 2204 | |||
2211 | 2205 | |||
2212 | 2206 | /** | ||
2213 | 2207 | * thunar_file_get_user: | ||
2214 | 2208 | * @file : a #ThunarFile instance. | ||
2215 | 2209 | * | ||
2216 | 2210 | * Determines the #ThunarUser for @file. If there's no | ||
2217 | 2211 | * user associated with @file or if the system is unable | ||
2218 | 2212 | * to determine the user, %NULL will be returned. | ||
2219 | 2213 | * | ||
2220 | 2214 | * The caller is responsible for freeing the returned object | ||
2221 | 2215 | * using g_object_unref(). | ||
2222 | 2216 | * | ||
2223 | 2217 | * Return value: the #ThunarUser for @file or %NULL. | ||
2224 | 2218 | **/ | ||
2225 | 2219 | ThunarUser* | ||
2226 | 2220 | thunar_file_get_user (const ThunarFile *file) | ||
2227 | 2221 | { | ||
2228 | 2222 | guint32 uid; | ||
2229 | 2223 | |||
2230 | 2224 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2231 | 2225 | |||
2232 | 2226 | /* TODO what are we going to do on non-UNIX systems? */ | ||
2233 | 2227 | uid = g_file_info_get_attribute_uint32 (file->info, | ||
2234 | 2228 | G_FILE_ATTRIBUTE_UNIX_UID); | ||
2235 | 2229 | |||
2236 | 2230 | return thunar_user_manager_get_user_by_id (user_manager, uid); | ||
2237 | 2231 | } | ||
2238 | 2232 | |||
2239 | 2233 | |||
2240 | 2234 | |||
2241 | 2235 | /** | ||
2242 | 2236 | * thunar_file_get_content_type: | ||
2243 | 2237 | * @file : a #ThunarFile. | ||
2244 | 2238 | * | ||
2245 | 2239 | * Returns the content type of @file. | ||
2246 | 2240 | * | ||
2247 | 2241 | * Return value: content type of @file. | ||
2248 | 2242 | **/ | ||
2249 | 2243 | const gchar * | ||
2250 | 2244 | thunar_file_get_content_type (ThunarFile *file) | ||
2251 | 2245 | { | ||
2252 | 2246 | GFileInfo *info; | ||
2253 | 2247 | GError *err = NULL; | ||
2254 | 2248 | const gchar *content_type = NULL; | ||
2255 | 2249 | |||
2256 | 2250 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2257 | 2251 | |||
2258 | 2252 | if (G_UNLIKELY (file->content_type == NULL)) | ||
2259 | 2253 | { | ||
2260 | 2254 | G_LOCK (file_content_type_mutex); | ||
2261 | 2255 | |||
2262 | 2256 | /* make sure we weren't waiting for a lock */ | ||
2263 | 2257 | if (G_UNLIKELY (file->content_type != NULL)) | ||
2264 | 2258 | goto bailout; | ||
2265 | 2259 | |||
2266 | 2260 | /* make sure this is not loaded in the general info */ | ||
2267 | 2261 | _thunar_assert (file->info == NULL | ||
2268 | 2262 | || !g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)); | ||
2269 | 2263 | |||
2270 | 2264 | if (G_UNLIKELY (file->kind == G_FILE_TYPE_DIRECTORY)) | ||
2271 | 2265 | { | ||
2272 | 2266 | /* this we known for sure */ | ||
2273 | 2267 | file->content_type = g_strdup ("inode/directory"); | ||
2274 | 2268 | } | ||
2275 | 2269 | else | ||
2276 | 2270 | { | ||
2277 | 2271 | /* async load the content-type */ | ||
2278 | 2272 | info = g_file_query_info (file->gfile, | ||
2279 | 2273 | G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, | ||
2280 | 2274 | G_FILE_QUERY_INFO_NONE, | ||
2281 | 2275 | NULL, &err); | ||
2282 | 2276 | |||
2283 | 2277 | if (G_LIKELY (info != NULL)) | ||
2284 | 2278 | { | ||
2285 | 2279 | /* store the new content type */ | ||
2286 | 2280 | content_type = g_file_info_get_content_type (info); | ||
2287 | 2281 | if (G_UNLIKELY (content_type != NULL)) | ||
2288 | 2282 | file->content_type = g_strdup (content_type); | ||
2289 | 2283 | g_object_unref (G_OBJECT (info)); | ||
2290 | 2284 | } | ||
2291 | 2285 | else | ||
2292 | 2286 | { | ||
2293 | 2287 | g_warning ("Content type loading failed for %s: %s", | ||
2294 | 2288 | thunar_file_get_display_name (file), | ||
2295 | 2289 | err->message); | ||
2296 | 2290 | g_error_free (err); | ||
2297 | 2291 | } | ||
2298 | 2292 | |||
2299 | 2293 | /* always provide a fallback */ | ||
2300 | 2294 | if (file->content_type == NULL) | ||
2301 | 2295 | file->content_type = g_strdup (DEFAULT_CONTENT_TYPE); | ||
2302 | 2296 | } | ||
2303 | 2297 | |||
2304 | 2298 | bailout: | ||
2305 | 2299 | |||
2306 | 2300 | G_UNLOCK (file_content_type_mutex); | ||
2307 | 2301 | } | ||
2308 | 2302 | |||
2309 | 2303 | return file->content_type; | ||
2310 | 2304 | } | ||
2311 | 2305 | |||
2312 | 2306 | |||
2313 | 2307 | |||
2314 | 2308 | gboolean | ||
2315 | 2309 | thunar_file_load_content_type (ThunarFile *file) | ||
2316 | 2310 | { | ||
2317 | 2311 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), TRUE); | ||
2318 | 2312 | |||
2319 | 2313 | if (file->content_type != NULL) | ||
2320 | 2314 | return FALSE; | ||
2321 | 2315 | |||
2322 | 2316 | thunar_file_get_content_type (file); | ||
2323 | 2317 | |||
2324 | 2318 | return TRUE; | ||
2325 | 2319 | } | ||
2326 | 2320 | |||
2327 | 2321 | |||
2328 | 2322 | |||
2329 | 2323 | /** | ||
2330 | 2324 | * thunar_file_get_symlink_target: | ||
2331 | 2325 | * @file : a #ThunarFile. | ||
2332 | 2326 | * | ||
2333 | 2327 | * Returns the path of the symlink target or %NULL if the @file | ||
2334 | 2328 | * is not a symlink. | ||
2335 | 2329 | * | ||
2336 | 2330 | * Return value: path of the symlink target or %NULL. | ||
2337 | 2331 | **/ | ||
2338 | 2332 | const gchar * | ||
2339 | 2333 | thunar_file_get_symlink_target (const ThunarFile *file) | ||
2340 | 2334 | { | ||
2341 | 2335 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2342 | 2336 | |||
2343 | 2337 | if (file->info == NULL) | ||
2344 | 2338 | return NULL; | ||
2345 | 2339 | |||
2346 | 2340 | return g_file_info_get_symlink_target (file->info); | ||
2347 | 2341 | } | ||
2348 | 2342 | |||
2349 | 2343 | |||
2350 | 2344 | |||
2351 | 2345 | /** | ||
2352 | 2346 | * thunar_file_get_basename: | ||
2353 | 2347 | * @file : a #ThunarFile. | ||
2354 | 2348 | * | ||
2355 | 2349 | * Returns the basename of the @file in UTF-8 encoding. | ||
2356 | 2350 | * | ||
2357 | 2351 | * Return value: UTF-8 encoded basename of the @file. | ||
2358 | 2352 | **/ | ||
2359 | 2353 | const gchar * | ||
2360 | 2354 | thunar_file_get_basename (const ThunarFile *file) | ||
2361 | 2355 | { | ||
2362 | 2356 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2363 | 2357 | return file->basename; | ||
2364 | 2358 | } | ||
2365 | 2359 | |||
2366 | 2360 | |||
2367 | 2361 | |||
2368 | 2362 | /** | ||
2369 | 2363 | * thunar_file_is_symlink: | ||
2370 | 2364 | * @file : a #ThunarFile. | ||
2371 | 2365 | * | ||
2372 | 2366 | * Returns %TRUE if @file is a symbolic link. | ||
2373 | 2367 | * | ||
2374 | 2368 | * Return value: %TRUE if @file is a symbolic link. | ||
2375 | 2369 | **/ | ||
2376 | 2370 | gboolean | ||
2377 | 2371 | thunar_file_is_symlink (const ThunarFile *file) | ||
2378 | 2372 | { | ||
2379 | 2373 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2380 | 2374 | |||
2381 | 2375 | if (file->info == NULL) | ||
2382 | 2376 | return FALSE; | ||
2383 | 2377 | |||
2384 | 2378 | return g_file_info_get_is_symlink (file->info); | ||
2385 | 2379 | } | ||
2386 | 2380 | |||
2387 | 2381 | |||
2388 | 2382 | |||
2389 | 2383 | /** | ||
2390 | 2384 | * thunar_file_get_size: | ||
2391 | 2385 | * @file : a #ThunarFile instance. | ||
2392 | 2386 | * | ||
2393 | 2387 | * Tries to determine the size of @file in bytes and | ||
2394 | 2388 | * returns the size. | ||
2395 | 2389 | * | ||
2396 | 2390 | * Return value: the size of @file in bytes. | ||
2397 | 2391 | **/ | ||
2398 | 2392 | guint64 | ||
2399 | 2393 | thunar_file_get_size (const ThunarFile *file) | ||
2400 | 2394 | { | ||
2401 | 2395 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0); | ||
2402 | 2396 | |||
2403 | 2397 | if (file->info == NULL) | ||
2404 | 2398 | return 0; | ||
2405 | 2399 | |||
2406 | 2400 | return g_file_info_get_size (file->info); | ||
2407 | 2401 | } | ||
2408 | 2402 | |||
2409 | 2403 | |||
2410 | 2404 | |||
2411 | 2405 | /** | ||
2412 | 2406 | * thunar_file_get_default_handler: | ||
2413 | 2407 | * @file : a #ThunarFile instance. | ||
2414 | 2408 | * | ||
2415 | 2409 | * Returns the default #GAppInfo for @file or %NULL if there is none. | ||
2416 | 2410 | * | ||
2417 | 2411 | * The caller is responsible to free the returned #GAppInfo using | ||
2418 | 2412 | * g_object_unref(). | ||
2419 | 2413 | * | ||
2420 | 2414 | * Return value: Default #GAppInfo for @file or %NULL if there is none. | ||
2421 | 2415 | **/ | ||
2422 | 2416 | GAppInfo * | ||
2423 | 2417 | thunar_file_get_default_handler (const ThunarFile *file) | ||
2424 | 2418 | { | ||
2425 | 2419 | const gchar *content_type; | ||
2426 | 2420 | GAppInfo *app_info = NULL; | ||
2427 | 2421 | gboolean must_support_uris = FALSE; | ||
2428 | 2422 | gchar *path; | ||
2429 | 2423 | |||
2430 | 2424 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2431 | 2425 | |||
2432 | 2426 | content_type = thunar_file_get_content_type (THUNAR_FILE (file)); | ||
2433 | 2427 | if (content_type != NULL) | ||
2434 | 2428 | { | ||
2435 | 2429 | path = g_file_get_path (file->gfile); | ||
2436 | 2430 | must_support_uris = (path == NULL); | ||
2437 | 2431 | g_free (path); | ||
2438 | 2432 | |||
2439 | 2433 | app_info = g_app_info_get_default_for_type (content_type, must_support_uris); | ||
2440 | 2434 | } | ||
2441 | 2435 | |||
2442 | 2436 | if (app_info == NULL) | ||
2443 | 2437 | app_info = g_file_query_default_handler (file->gfile, NULL, NULL); | ||
2444 | 2438 | |||
2445 | 2439 | return app_info; | ||
2446 | 2440 | } | ||
2447 | 2441 | |||
2448 | 2442 | |||
2449 | 2443 | |||
2450 | 2444 | /** | ||
2451 | 2445 | * thunar_file_get_kind: | ||
2452 | 2446 | * @file : a #ThunarFile instance. | ||
2453 | 2447 | * | ||
2454 | 2448 | * Returns the kind of @file. | ||
2455 | 2449 | * | ||
2456 | 2450 | * Return value: the kind of @file. | ||
2457 | 2451 | **/ | ||
2458 | 2452 | GFileType | ||
2459 | 2453 | thunar_file_get_kind (const ThunarFile *file) | ||
2460 | 2454 | { | ||
2461 | 2455 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), G_FILE_TYPE_UNKNOWN); | ||
2462 | 2456 | return file->kind; | ||
2463 | 2457 | } | ||
2464 | 2458 | |||
2465 | 2459 | |||
2466 | 2460 | |||
2467 | 2461 | GFile * | ||
2468 | 2462 | thunar_file_get_target_location (const ThunarFile *file) | ||
2469 | 2463 | { | ||
2470 | 2464 | const gchar *uri; | ||
2471 | 2465 | |||
2472 | 2466 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2473 | 2467 | |||
2474 | 2468 | if (file->info == NULL) | ||
2475 | 2469 | return g_object_ref (file->gfile); | ||
2476 | 2470 | |||
2477 | 2471 | uri = g_file_info_get_attribute_string (file->info, | ||
2478 | 2472 | G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); | ||
2479 | 2473 | |||
2480 | 2474 | return (uri != NULL) ? g_file_new_for_uri (uri) : NULL; | ||
2481 | 2475 | } | ||
2482 | 2476 | |||
2483 | 2477 | |||
2484 | 2478 | |||
2485 | 2479 | /** | ||
2486 | 2480 | * thunar_file_get_mode: | ||
2487 | 2481 | * @file : a #ThunarFile instance. | ||
2488 | 2482 | * | ||
2489 | 2483 | * Returns the permission bits of @file. | ||
2490 | 2484 | * | ||
2491 | 2485 | * Return value: the permission bits of @file. | ||
2492 | 2486 | **/ | ||
2493 | 2487 | ThunarFileMode | ||
2494 | 2488 | thunar_file_get_mode (const ThunarFile *file) | ||
2495 | 2489 | { | ||
2496 | 2490 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0); | ||
2497 | 2491 | |||
2498 | 2492 | if (file->info == NULL) | ||
2499 | 2493 | return 0; | ||
2500 | 2494 | |||
2501 | 2495 | if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_UNIX_MODE)) | ||
2502 | 2496 | return g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_MODE); | ||
2503 | 2497 | else | ||
2504 | 2498 | return thunar_file_is_directory (file) ? 0777 : 0666; | ||
2505 | 2499 | } | ||
2506 | 2500 | |||
2507 | 2501 | |||
2508 | 2502 | |||
2509 | 2503 | gboolean | ||
2510 | 2504 | thunar_file_is_mounted (const ThunarFile *file) | ||
2511 | 2505 | { | ||
2512 | 2506 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2513 | 2507 | return FLAG_IS_SET (file, THUNAR_FILE_FLAG_IS_MOUNTED); | ||
2514 | 2508 | } | ||
2515 | 2509 | |||
2516 | 2510 | |||
2517 | 2511 | |||
2518 | 2512 | gboolean | ||
2519 | 2513 | thunar_file_exists (const ThunarFile *file) | ||
2520 | 2514 | { | ||
2521 | 2515 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2522 | 2516 | return g_file_query_exists (file->gfile, NULL); | ||
2523 | 2517 | } | ||
2524 | 2518 | |||
2525 | 2519 | |||
2526 | 2520 | |||
2527 | 2521 | /** | ||
2528 | 2522 | * thunar_file_is_directory: | ||
2529 | 2523 | * @file : a #ThunarFile instance. | ||
2530 | 2524 | * | ||
2531 | 2525 | * Checks whether @file refers to a directory. | ||
2532 | 2526 | * | ||
2533 | 2527 | * Return value: %TRUE if @file is a directory. | ||
2534 | 2528 | **/ | ||
2535 | 2529 | gboolean | ||
2536 | 2530 | thunar_file_is_directory (const ThunarFile *file) | ||
2537 | 2531 | { | ||
2538 | 2532 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2539 | 2533 | return file->kind == G_FILE_TYPE_DIRECTORY; | ||
2540 | 2534 | } | ||
2541 | 2535 | |||
2542 | 2536 | |||
2543 | 2537 | |||
2544 | 2538 | /** | ||
2545 | 2539 | * thunar_file_is_shortcut: | ||
2546 | 2540 | * @file : a #ThunarFile instance. | ||
2547 | 2541 | * | ||
2548 | 2542 | * Checks whether @file refers to a shortcut to something else. | ||
2549 | 2543 | * | ||
2550 | 2544 | * Return value: %TRUE if @file is a shortcut. | ||
2551 | 2545 | **/ | ||
2552 | 2546 | gboolean | ||
2553 | 2547 | thunar_file_is_shortcut (const ThunarFile *file) | ||
2554 | 2548 | { | ||
2555 | 2549 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2556 | 2550 | return file->kind == G_FILE_TYPE_SHORTCUT; | ||
2557 | 2551 | } | ||
2558 | 2552 | |||
2559 | 2553 | |||
2560 | 2554 | |||
2561 | 2555 | /** | ||
2562 | 2556 | * thunar_file_is_mountable: | ||
2563 | 2557 | * @file : a #ThunarFile instance. | ||
2564 | 2558 | * | ||
2565 | 2559 | * Checks whether @file refers to a mountable file/directory. | ||
2566 | 2560 | * | ||
2567 | 2561 | * Return value: %TRUE if @file is a mountable file/directory. | ||
2568 | 2562 | **/ | ||
2569 | 2563 | gboolean | ||
2570 | 2564 | thunar_file_is_mountable (const ThunarFile *file) | ||
2571 | 2565 | { | ||
2572 | 2566 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2573 | 2567 | return file->kind == G_FILE_TYPE_MOUNTABLE; | ||
2574 | 2568 | } | ||
2575 | 2569 | |||
2576 | 2570 | |||
2577 | 2571 | |||
2578 | 2572 | /** | ||
2579 | 2573 | * thunar_file_is_local: | ||
2580 | 2574 | * @file : a #ThunarFile instance. | ||
2581 | 2575 | * | ||
2582 | 2576 | * Returns %TRUE if @file is a local file with the | ||
2583 | 2577 | * file:// URI scheme. | ||
2584 | 2578 | * | ||
2585 | 2579 | * Return value: %TRUE if @file is local. | ||
2586 | 2580 | **/ | ||
2587 | 2581 | gboolean | ||
2588 | 2582 | thunar_file_is_local (const ThunarFile *file) | ||
2589 | 2583 | { | ||
2590 | 2584 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2591 | 2585 | return g_file_has_uri_scheme (file->gfile, "file"); | ||
2592 | 2586 | } | ||
2593 | 2587 | |||
2594 | 2588 | |||
2595 | 2589 | |||
2596 | 2590 | /** | ||
2597 | 2591 | * thunar_file_is_parent: | ||
2598 | 2592 | * @file : a #ThunarFile instance. | ||
2599 | 2593 | * @child : another #ThunarFile instance. | ||
2600 | 2594 | * | ||
2601 | 2595 | * Determines whether @file is the parent directory of @child. | ||
2602 | 2596 | * | ||
2603 | 2597 | * Return value: %TRUE if @file is the parent of @child. | ||
2604 | 2598 | **/ | ||
2605 | 2599 | gboolean | ||
2606 | 2600 | thunar_file_is_parent (const ThunarFile *file, | ||
2607 | 2601 | const ThunarFile *child) | ||
2608 | 2602 | { | ||
2609 | 2603 | gboolean is_parent = FALSE; | ||
2610 | 2604 | GFile *parent; | ||
2611 | 2605 | |||
2612 | 2606 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2613 | 2607 | _thunar_return_val_if_fail (THUNAR_IS_FILE (child), FALSE); | ||
2614 | 2608 | |||
2615 | 2609 | parent = g_file_get_parent (child->gfile); | ||
2616 | 2610 | if (parent != NULL) | ||
2617 | 2611 | { | ||
2618 | 2612 | is_parent = g_file_equal (file->gfile, parent); | ||
2619 | 2613 | g_object_unref (parent); | ||
2620 | 2614 | } | ||
2621 | 2615 | |||
2622 | 2616 | return is_parent; | ||
2623 | 2617 | } | ||
2624 | 2618 | |||
2625 | 2619 | |||
2626 | 2620 | |||
2627 | 2621 | /** | ||
2628 | 2622 | * thunar_file_is_ancestor: | ||
2629 | 2623 | * @file : a #ThunarFile instance. | ||
2630 | 2624 | * @ancestor : another #GFile instance. | ||
2631 | 2625 | * | ||
2632 | 2626 | * Determines whether @file is somewhere inside @ancestor, | ||
2633 | 2627 | * possibly with intermediate folders. | ||
2634 | 2628 | * | ||
2635 | 2629 | * Return value: %TRUE if @ancestor contains @file as a | ||
2636 | 2630 | * child, grandchild, great grandchild, etc. | ||
2637 | 2631 | **/ | ||
2638 | 2632 | gboolean | ||
2639 | 2633 | thunar_file_is_gfile_ancestor (const ThunarFile *file, | ||
2640 | 2634 | GFile *ancestor) | ||
2641 | 2635 | { | ||
2642 | 2636 | gboolean is_ancestor = FALSE; | ||
2643 | 2637 | GFile *current = NULL; | ||
2644 | 2638 | GFile *tmp; | ||
2645 | 2639 | |||
2646 | 2640 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2647 | 2641 | _thunar_return_val_if_fail (G_IS_FILE (ancestor), FALSE); | ||
2648 | 2642 | |||
2649 | 2643 | for (current = g_object_ref (file->gfile); | ||
2650 | 2644 | is_ancestor == FALSE && current != NULL; | ||
2651 | 2645 | tmp = g_file_get_parent (current), g_object_unref (current), current = tmp) | ||
2652 | 2646 | { | ||
2653 | 2647 | if (G_UNLIKELY (g_file_equal (current, ancestor))) | ||
2654 | 2648 | is_ancestor = TRUE; | ||
2655 | 2649 | } | ||
2656 | 2650 | |||
2657 | 2651 | if (current != NULL) | ||
2658 | 2652 | g_object_unref (current); | ||
2659 | 2653 | |||
2660 | 2654 | return is_ancestor; | ||
2661 | 2655 | } | ||
2662 | 2656 | |||
2663 | 2657 | |||
2664 | 2658 | |||
2665 | 2659 | /** | ||
2666 | 2660 | * thunar_file_is_ancestor: | ||
2667 | 2661 | * @file : a #ThunarFile instance. | ||
2668 | 2662 | * @ancestor : another #ThunarFile instance. | ||
2669 | 2663 | * | ||
2670 | 2664 | * Determines whether @file is somewhere inside @ancestor, | ||
2671 | 2665 | * possibly with intermediate folders. | ||
2672 | 2666 | * | ||
2673 | 2667 | * Return value: %TRUE if @ancestor contains @file as a | ||
2674 | 2668 | * child, grandchild, great grandchild, etc. | ||
2675 | 2669 | **/ | ||
2676 | 2670 | gboolean | ||
2677 | 2671 | thunar_file_is_ancestor (const ThunarFile *file, | ||
2678 | 2672 | const ThunarFile *ancestor) | ||
2679 | 2673 | { | ||
2680 | 2674 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2681 | 2675 | _thunar_return_val_if_fail (THUNAR_IS_FILE (ancestor), FALSE); | ||
2682 | 2676 | |||
2683 | 2677 | return thunar_file_is_gfile_ancestor (file, ancestor->gfile); | ||
2684 | 2678 | } | ||
2685 | 2679 | |||
2686 | 2680 | |||
2687 | 2681 | |||
2688 | 2682 | /** | ||
2689 | 2683 | * thunar_file_is_executable: | ||
2690 | 2684 | * @file : a #ThunarFile instance. | ||
2691 | 2685 | * | ||
2692 | 2686 | * Determines whether the owner of the current process is allowed | ||
2693 | 2687 | * to execute the @file (or enter the directory refered to by | ||
2694 | 2688 | * @file). On UNIX it also returns %TRUE if @file refers to a | ||
2695 | 2689 | * desktop entry. | ||
2696 | 2690 | * | ||
2697 | 2691 | * Return value: %TRUE if @file can be executed. | ||
2698 | 2692 | **/ | ||
2699 | 2693 | gboolean | ||
2700 | 2694 | thunar_file_is_executable (const ThunarFile *file) | ||
2701 | 2695 | { | ||
2702 | 2696 | gboolean can_execute = FALSE; | ||
2703 | 2697 | const gchar *content_type; | ||
2704 | 2698 | |||
2705 | 2699 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2706 | 2700 | |||
2707 | 2701 | if (file->info == NULL) | ||
2708 | 2702 | return FALSE; | ||
2709 | 2703 | |||
2710 | 2704 | if (g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) | ||
2711 | 2705 | { | ||
2712 | 2706 | /* get the content type of the file */ | ||
2713 | 2707 | content_type = thunar_file_get_content_type (THUNAR_FILE (file)); | ||
2714 | 2708 | if (G_LIKELY (content_type != NULL)) | ||
2715 | 2709 | { | ||
2716 | 2710 | #ifdef G_OS_WIN32 | ||
2717 | 2711 | /* check for .exe, .bar or .com */ | ||
2718 | 2712 | can_execute = g_content_type_can_be_executable (content_type); | ||
2719 | 2713 | #else | ||
2720 | 2714 | /* check if the content type is save to execute, we don't use | ||
2721 | 2715 | * g_content_type_can_be_executable() for unix because it also returns | ||
2722 | 2716 | * true for "text/plain" and we don't want that */ | ||
2723 | 2717 | if (g_content_type_is_a (content_type, "application/x-executable") | ||
2724 | 2718 | || g_content_type_is_a (content_type, "application/x-shellscript")) | ||
2725 | 2719 | { | ||
2726 | 2720 | can_execute = TRUE; | ||
2727 | 2721 | } | ||
2728 | 2722 | #endif | ||
2729 | 2723 | } | ||
2730 | 2724 | } | ||
2731 | 2725 | |||
2732 | 2726 | return can_execute || thunar_file_is_desktop_file (file, NULL); | ||
2733 | 2727 | } | ||
2734 | 2728 | |||
2735 | 2729 | |||
2736 | 2730 | |||
2737 | 2731 | /** | ||
2738 | 2732 | * thunar_file_is_readable: | ||
2739 | 2733 | * @file : a #ThunarFile instance. | ||
2740 | 2734 | * | ||
2741 | 2735 | * Determines whether the owner of the current process is allowed | ||
2742 | 2736 | * to read the @file. | ||
2743 | 2737 | * | ||
2744 | 2738 | * Return value: %TRUE if @file can be read. | ||
2745 | 2739 | **/ | ||
2746 | 2740 | static gboolean | ||
2747 | 2741 | thunar_file_is_readable (const ThunarFile *file) | ||
2748 | 2742 | { | ||
2749 | 2743 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2750 | 2744 | |||
2751 | 2745 | if (file->info == NULL) | ||
2752 | 2746 | return FALSE; | ||
2753 | 2747 | |||
2754 | 2748 | if (!g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) | ||
2755 | 2749 | return TRUE; | ||
2756 | 2750 | |||
2757 | 2751 | return g_file_info_get_attribute_boolean (file->info, | ||
2758 | 2752 | G_FILE_ATTRIBUTE_ACCESS_CAN_READ); | ||
2759 | 2753 | } | ||
2760 | 2754 | |||
2761 | 2755 | |||
2762 | 2756 | |||
2763 | 2757 | /** | ||
2764 | 2758 | * thunar_file_is_writable: | ||
2765 | 2759 | * @file : a #ThunarFile instance. | ||
2766 | 2760 | * | ||
2767 | 2761 | * Determines whether the owner of the current process is allowed | ||
2768 | 2762 | * to write the @file. | ||
2769 | 2763 | * | ||
2770 | 2764 | * Return value: %TRUE if @file can be read. | ||
2771 | 2765 | **/ | ||
2772 | 2766 | gboolean | ||
2773 | 2767 | thunar_file_is_writable (const ThunarFile *file) | ||
2774 | 2768 | { | ||
2775 | 2769 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2776 | 2770 | |||
2777 | 2771 | if (file->info == NULL) | ||
2778 | 2772 | return FALSE; | ||
2779 | 2773 | |||
2780 | 2774 | if (!g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) | ||
2781 | 2775 | return TRUE; | ||
2782 | 2776 | |||
2783 | 2777 | return g_file_info_get_attribute_boolean (file->info, | ||
2784 | 2778 | G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); | ||
2785 | 2779 | } | ||
2786 | 2780 | |||
2787 | 2781 | |||
2788 | 2782 | |||
2789 | 2783 | /** | ||
2790 | 2784 | * thunar_file_is_hidden: | ||
2791 | 2785 | * @file : a #ThunarFile instance. | ||
2792 | 2786 | * | ||
2793 | 2787 | * Checks whether @file can be considered a hidden file. | ||
2794 | 2788 | * | ||
2795 | 2789 | * Return value: %TRUE if @file is a hidden file, else %FALSE. | ||
2796 | 2790 | **/ | ||
2797 | 2791 | gboolean | ||
2798 | 2792 | thunar_file_is_hidden (const ThunarFile *file) | ||
2799 | 2793 | { | ||
2800 | 2794 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2801 | 2795 | |||
2802 | 2796 | if (file->info == NULL) | ||
2803 | 2797 | return FALSE; | ||
2804 | 2798 | |||
2805 | 2799 | return g_file_info_get_is_hidden (file->info) | ||
2806 | 2800 | || g_file_info_get_is_backup (file->info); | ||
2807 | 2801 | } | ||
2808 | 2802 | |||
2809 | 2803 | |||
2810 | 2804 | |||
2811 | 2805 | /** | ||
2812 | 2806 | * thunar_file_is_home: | ||
2813 | 2807 | * @file : a #ThunarFile. | ||
2814 | 2808 | * | ||
2815 | 2809 | * Checks whether @file refers to the users home directory. | ||
2816 | 2810 | * | ||
2817 | 2811 | * Return value: %TRUE if @file is the users home directory. | ||
2818 | 2812 | **/ | ||
2819 | 2813 | gboolean | ||
2820 | 2814 | thunar_file_is_home (const ThunarFile *file) | ||
2821 | 2815 | { | ||
2822 | 2816 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2823 | 2817 | return thunar_g_file_is_home (file->gfile); | ||
2824 | 2818 | } | ||
2825 | 2819 | |||
2826 | 2820 | |||
2827 | 2821 | |||
2828 | 2822 | /** | ||
2829 | 2823 | * thunar_file_is_regular: | ||
2830 | 2824 | * @file : a #ThunarFile. | ||
2831 | 2825 | * | ||
2832 | 2826 | * Checks whether @file refers to a regular file. | ||
2833 | 2827 | * | ||
2834 | 2828 | * Return value: %TRUE if @file is a regular file. | ||
2835 | 2829 | **/ | ||
2836 | 2830 | gboolean | ||
2837 | 2831 | thunar_file_is_regular (const ThunarFile *file) | ||
2838 | 2832 | { | ||
2839 | 2833 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2840 | 2834 | return file->kind == G_FILE_TYPE_REGULAR; | ||
2841 | 2835 | } | ||
2842 | 2836 | |||
2843 | 2837 | |||
2844 | 2838 | |||
2845 | 2839 | /** | ||
2846 | 2840 | * thunar_file_is_trashed: | ||
2847 | 2841 | * @file : a #ThunarFile instance. | ||
2848 | 2842 | * | ||
2849 | 2843 | * Returns %TRUE if @file is a local file that resides in | ||
2850 | 2844 | * the trash bin. | ||
2851 | 2845 | * | ||
2852 | 2846 | * Return value: %TRUE if @file is in the trash, or | ||
2853 | 2847 | * the trash folder itself. | ||
2854 | 2848 | **/ | ||
2855 | 2849 | gboolean | ||
2856 | 2850 | thunar_file_is_trashed (const ThunarFile *file) | ||
2857 | 2851 | { | ||
2858 | 2852 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2859 | 2853 | return thunar_g_file_is_trashed (file->gfile); | ||
2860 | 2854 | } | ||
2861 | 2855 | |||
2862 | 2856 | |||
2863 | 2857 | |||
2864 | 2858 | /** | ||
2865 | 2859 | * thunar_file_is_desktop_file: | ||
2866 | 2860 | * @file : a #ThunarFile. | ||
2867 | 2861 | * @is_secure : if %NULL do a simple check, else it will set this boolean | ||
2868 | 2862 | * to indicate if the desktop file is safe see bug #5012 | ||
2869 | 2863 | * for more info. | ||
2870 | 2864 | * | ||
2871 | 2865 | * Returns %TRUE if @file is a .desktop file. The @is_secure return value | ||
2872 | 2866 | * will tell if the .desktop file is also secure. | ||
2873 | 2867 | * | ||
2874 | 2868 | * Return value: %TRUE if @file is a .desktop file. | ||
2875 | 2869 | **/ | ||
2876 | 2870 | gboolean | ||
2877 | 2871 | thunar_file_is_desktop_file (const ThunarFile *file, | ||
2878 | 2872 | gboolean *is_secure) | ||
2879 | 2873 | { | ||
2880 | 2874 | const gchar * const *data_dirs; | ||
2881 | 2875 | guint n; | ||
2882 | 2876 | gchar *path; | ||
2883 | 2877 | |||
2884 | 2878 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2885 | 2879 | |||
2886 | 2880 | if (file->info == NULL) | ||
2887 | 2881 | return FALSE; | ||
2888 | 2882 | |||
2889 | 2883 | /* only allow regular files with a .desktop extension */ | ||
2890 | 2884 | if (!g_str_has_suffix (file->basename, ".desktop") | ||
2891 | 2885 | || file->kind != G_FILE_TYPE_REGULAR) | ||
2892 | 2886 | return FALSE; | ||
2893 | 2887 | |||
2894 | 2888 | /* don't check more if not needed */ | ||
2895 | 2889 | if (is_secure == NULL) | ||
2896 | 2890 | return TRUE; | ||
2897 | 2891 | |||
2898 | 2892 | /* desktop files outside xdg directories need to be executable for security reasons */ | ||
2899 | 2893 | if (g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) | ||
2900 | 2894 | { | ||
2901 | 2895 | /* has +x */ | ||
2902 | 2896 | *is_secure = TRUE; | ||
2903 | 2897 | } | ||
2904 | 2898 | else | ||
2905 | 2899 | { | ||
2906 | 2900 | /* assume the file is not safe */ | ||
2907 | 2901 | *is_secure = FALSE; | ||
2908 | 2902 | |||
2909 | 2903 | /* deskopt files in xdg directories are also fine... */ | ||
2910 | 2904 | if (g_file_is_native (thunar_file_get_file (file))) | ||
2911 | 2905 | { | ||
2912 | 2906 | data_dirs = g_get_system_data_dirs (); | ||
2913 | 2907 | if (G_LIKELY (data_dirs != NULL)) | ||
2914 | 2908 | { | ||
2915 | 2909 | path = g_file_get_path (thunar_file_get_file (file)); | ||
2916 | 2910 | for (n = 0; data_dirs[n] != NULL; n++) | ||
2917 | 2911 | { | ||
2918 | 2912 | if (g_str_has_prefix (path, data_dirs[n])) | ||
2919 | 2913 | { | ||
2920 | 2914 | /* has known prefix, can launch without problems */ | ||
2921 | 2915 | *is_secure = TRUE; | ||
2922 | 2916 | break; | ||
2923 | 2917 | } | ||
2924 | 2918 | } | ||
2925 | 2919 | g_free (path); | ||
2926 | 2920 | } | ||
2927 | 2921 | } | ||
2928 | 2922 | } | ||
2929 | 2923 | |||
2930 | 2924 | return TRUE; | ||
2931 | 2925 | } | ||
2932 | 2926 | |||
2933 | 2927 | |||
2934 | 2928 | |||
2935 | 2929 | /** | ||
2936 | 2930 | * thunar_file_get_display_name: | ||
2937 | 2931 | * @file : a #ThunarFile instance. | ||
2938 | 2932 | * | ||
2939 | 2933 | * Returns the @file name in the UTF-8 encoding, which is | ||
2940 | 2934 | * suitable for displaying the file name in the GUI. | ||
2941 | 2935 | * | ||
2942 | 2936 | * Return value: the @file name suitable for display. | ||
2943 | 2937 | **/ | ||
2944 | 2938 | const gchar * | ||
2945 | 2939 | thunar_file_get_display_name (const ThunarFile *file) | ||
2946 | 2940 | { | ||
2947 | 2941 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
2948 | 2942 | return file->display_name; | ||
2949 | 2943 | } | ||
2950 | 2944 | |||
2951 | 2945 | |||
2952 | 2946 | |||
2953 | 2947 | /** | ||
2954 | 2948 | * thunar_file_get_deletion_date: | ||
2955 | 2949 | * @file : a #ThunarFile instance. | ||
2956 | 2950 | * @date_style : the style used to format the date. | ||
2957 | 2951 | * | ||
2958 | 2952 | * Returns the deletion date of the @file if the @file | ||
2959 | 2953 | * is located in the trash. Otherwise %NULL will be | ||
2960 | 2954 | * returned. | ||
2961 | 2955 | * | ||
2962 | 2956 | * The caller is responsible to free the returned string | ||
2963 | 2957 | * using g_free() when no longer needed. | ||
2964 | 2958 | * | ||
2965 | 2959 | * Return value: the deletion date of @file if @file is | ||
2966 | 2960 | * in the trash, %NULL otherwise. | ||
2967 | 2961 | **/ | ||
2968 | 2962 | gchar* | ||
2969 | 2963 | thunar_file_get_deletion_date (const ThunarFile *file, | ||
2970 | 2964 | ThunarDateStyle date_style) | ||
2971 | 2965 | { | ||
2972 | 2966 | const gchar *date; | ||
2973 | 2967 | time_t deletion_time; | ||
2974 | 2968 | |||
2975 | 2969 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
2976 | 2970 | _thunar_return_val_if_fail (G_IS_FILE_INFO (file->info), NULL); | ||
2977 | 2971 | |||
2978 | 2972 | date = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE); | ||
2979 | 2973 | if (G_UNLIKELY (date == NULL)) | ||
2980 | 2974 | return NULL; | ||
2981 | 2975 | |||
2982 | 2976 | /* try to parse the DeletionDate (RFC 3339 string) */ | ||
2983 | 2977 | deletion_time = thunar_util_time_from_rfc3339 (date); | ||
2984 | 2978 | |||
2985 | 2979 | /* humanize the time value */ | ||
2986 | 2980 | return thunar_util_humanize_file_time (deletion_time, date_style); | ||
2987 | 2981 | } | ||
2988 | 2982 | |||
2989 | 2983 | |||
2990 | 2984 | |||
2991 | 2985 | /** | ||
2992 | 2986 | * thunar_file_get_original_path: | ||
2993 | 2987 | * @file : a #ThunarFile instance. | ||
2994 | 2988 | * | ||
2995 | 2989 | * Returns the original path of the @file if the @file | ||
2996 | 2990 | * is located in the trash. Otherwise %NULL will be | ||
2997 | 2991 | * returned. | ||
2998 | 2992 | * | ||
2999 | 2993 | * Return value: the original path of @file if @file is | ||
3000 | 2994 | * in the trash, %NULL otherwise. | ||
3001 | 2995 | **/ | ||
3002 | 2996 | const gchar * | ||
3003 | 2997 | thunar_file_get_original_path (const ThunarFile *file) | ||
3004 | 2998 | { | ||
3005 | 2999 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
3006 | 3000 | |||
3007 | 3001 | if (file->info == NULL) | ||
3008 | 3002 | return NULL; | ||
3009 | 3003 | |||
3010 | 3004 | return g_file_info_get_attribute_byte_string (file->info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH); | ||
3011 | 3005 | } | ||
3012 | 3006 | |||
3013 | 3007 | |||
3014 | 3008 | |||
3015 | 3009 | /** | ||
3016 | 3010 | * thunar_file_get_item_count: | ||
3017 | 3011 | * @file : a #ThunarFile instance. | ||
3018 | 3012 | * | ||
3019 | 3013 | * Returns the number of items in the trash, if @file refers to the | ||
3020 | 3014 | * trash root directory. Otherwise returns 0. | ||
3021 | 3015 | * | ||
3022 | 3016 | * Return value: number of files in the trash if @file is the trash | ||
3023 | 3017 | * root dir, 0 otherwise. | ||
3024 | 3018 | **/ | ||
3025 | 3019 | guint32 | ||
3026 | 3020 | thunar_file_get_item_count (const ThunarFile *file) | ||
3027 | 3021 | { | ||
3028 | 3022 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0); | ||
3029 | 3023 | |||
3030 | 3024 | if (file->info == NULL) | ||
3031 | 3025 | return 0; | ||
3032 | 3026 | |||
3033 | 3027 | return g_file_info_get_attribute_uint32 (file->info, | ||
3034 | 3028 | G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT); | ||
3035 | 3029 | } | ||
3036 | 3030 | |||
3037 | 3031 | |||
3038 | 3032 | |||
3039 | 3033 | /** | ||
3040 | 3034 | * thunar_file_is_chmodable: | ||
3041 | 3035 | * @file : a #ThunarFile instance. | ||
3042 | 3036 | * | ||
3043 | 3037 | * Determines whether the owner of the current process is allowed | ||
3044 | 3038 | * to changed the file mode of @file. | ||
3045 | 3039 | * | ||
3046 | 3040 | * Return value: %TRUE if the mode of @file can be changed. | ||
3047 | 3041 | **/ | ||
3048 | 3042 | gboolean | ||
3049 | 3043 | thunar_file_is_chmodable (const ThunarFile *file) | ||
3050 | 3044 | { | ||
3051 | 3045 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
3052 | 3046 | |||
3053 | 3047 | /* we can only change the mode if we the euid is | ||
3054 | 3048 | * a) equal to the file owner id | ||
3055 | 3049 | * or | ||
3056 | 3050 | * b) the super-user id | ||
3057 | 3051 | * and the file is not in the trash. | ||
3058 | 3052 | */ | ||
3059 | 3053 | if (file->info == NULL) | ||
3060 | 3054 | { | ||
3061 | 3055 | return (effective_user_id == 0 && !thunar_file_is_trashed (file)); | ||
3062 | 3056 | } | ||
3063 | 3057 | else | ||
3064 | 3058 | { | ||
3065 | 3059 | return ((effective_user_id == 0 | ||
3066 | 3060 | || effective_user_id == g_file_info_get_attribute_uint32 (file->info, | ||
3067 | 3061 | G_FILE_ATTRIBUTE_UNIX_UID)) | ||
3068 | 3062 | && !thunar_file_is_trashed (file)); | ||
3069 | 3063 | } | ||
3070 | 3064 | } | ||
3071 | 3065 | |||
3072 | 3066 | |||
3073 | 3067 | |||
3074 | 3068 | /** | ||
3075 | 3069 | * thunar_file_is_renameable: | ||
3076 | 3070 | * @file : a #ThunarFile instance. | ||
3077 | 3071 | * | ||
3078 | 3072 | * Determines whether @file can be renamed using | ||
3079 | 3073 | * #thunar_file_rename(). Note that the return | ||
3080 | 3074 | * value is just a guess and #thunar_file_rename() | ||
3081 | 3075 | * may fail even if this method returns %TRUE. | ||
3082 | 3076 | * | ||
3083 | 3077 | * Return value: %TRUE if @file can be renamed. | ||
3084 | 3078 | **/ | ||
3085 | 3079 | gboolean | ||
3086 | 3080 | thunar_file_is_renameable (const ThunarFile *file) | ||
3087 | 3081 | { | ||
3088 | 3082 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
3089 | 3083 | |||
3090 | 3084 | if (file->info == NULL) | ||
3091 | 3085 | return FALSE; | ||
3092 | 3086 | |||
3093 | 3087 | return g_file_info_get_attribute_boolean (file->info, | ||
3094 | 3088 | G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME); | ||
3095 | 3089 | } | ||
3096 | 3090 | |||
3097 | 3091 | |||
3098 | 3092 | |||
3099 | 3093 | gboolean | ||
3100 | 3094 | thunar_file_can_be_trashed (const ThunarFile *file) | ||
3101 | 3095 | { | ||
3102 | 3096 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
3103 | 3097 | |||
3104 | 3098 | if (file->info == NULL) | ||
3105 | 3099 | return FALSE; | ||
3106 | 3100 | |||
3107 | 3101 | return g_file_info_get_attribute_boolean (file->info, | ||
3108 | 3102 | G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH); | ||
3109 | 3103 | } | ||
3110 | 3104 | |||
3111 | 3105 | |||
3112 | 3106 | |||
3113 | 3107 | /** | ||
3114 | 3108 | * thunar_file_get_emblem_names: | ||
3115 | 3109 | * @file : a #ThunarFile instance. | ||
3116 | 3110 | * | ||
3117 | 3111 | * Determines the names of the emblems that should be displayed for | ||
3118 | 3112 | * @file. The returned list is owned by the caller, but the list | ||
3119 | 3113 | * items - the name strings - are owned by @file. So the caller | ||
3120 | 3114 | * must call g_list_free(), but don't g_free() the list items. | ||
3121 | 3115 | * | ||
3122 | 3116 | * Note that the strings contained in the returned list are | ||
3123 | 3117 | * not garantied to exist over the next iteration of the main | ||
3124 | 3118 | * loop. So in case you need the list of emblem names for | ||
3125 | 3119 | * a longer time, you'll need to take a copy of the strings. | ||
3126 | 3120 | * | ||
3127 | 3121 | * Return value: the names of the emblems for @file. | ||
3128 | 3122 | **/ | ||
3129 | 3123 | GList* | ||
3130 | 3124 | thunar_file_get_emblem_names (ThunarFile *file) | ||
3131 | 3125 | { | ||
3132 | 3126 | guint32 uid; | ||
3133 | 3127 | gchar **emblem_names; | ||
3134 | 3128 | GList *emblems = NULL; | ||
3135 | 3129 | |||
3136 | 3130 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
3137 | 3131 | |||
3138 | 3132 | /* leave if there is no info */ | ||
3139 | 3133 | if (file->info == NULL) | ||
3140 | 3134 | return NULL; | ||
3141 | 3135 | |||
3142 | 3136 | /* determine the custom emblems */ | ||
3143 | 3137 | emblem_names = g_file_info_get_attribute_stringv (file->info, "metadata::emblems"); | ||
3144 | 3138 | if (G_UNLIKELY (emblem_names != NULL)) | ||
3145 | 3139 | { | ||
3146 | 3140 | for (; *emblem_names != NULL; ++emblem_names) | ||
3147 | 3141 | emblems = g_list_append (emblems, *emblem_names); | ||
3148 | 3142 | } | ||
3149 | 3143 | |||
3150 | 3144 | if (thunar_file_is_symlink (file)) | ||
3151 | 3145 | emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_SYMBOLIC_LINK); | ||
3152 | 3146 | |||
3153 | 3147 | /* determine the user ID of the file owner */ | ||
3154 | 3148 | /* TODO what are we going to do here on non-UNIX systems? */ | ||
3155 | 3149 | uid = file->info != NULL | ||
3156 | 3150 | ? g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_UID) | ||
3157 | 3151 | : 0; | ||
3158 | 3152 | |||
3159 | 3153 | /* we add "cant-read" if either (a) the file is not readable or (b) a directory, that lacks the | ||
3160 | 3154 | * x-bit, see http://bugzilla.xfce.org/show_bug.cgi?id=1408 for the details about this change. | ||
3161 | 3155 | */ | ||
3162 | 3156 | if (!thunar_file_is_readable (file) | ||
3163 | 3157 | || (thunar_file_is_directory (file) | ||
3164 | 3158 | && thunar_file_denies_access_permission (file, THUNAR_FILE_MODE_USR_EXEC, | ||
3165 | 3159 | THUNAR_FILE_MODE_GRP_EXEC, | ||
3166 | 3160 | THUNAR_FILE_MODE_OTH_EXEC))) | ||
3167 | 3161 | { | ||
3168 | 3162 | emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_CANT_READ); | ||
3169 | 3163 | } | ||
3170 | 3164 | else if (G_UNLIKELY (uid == effective_user_id && !thunar_file_is_writable (file))) | ||
3171 | 3165 | { | ||
3172 | 3166 | /* we own the file, but we cannot write to it, that's why we mark it as "cant-write", so | ||
3173 | 3167 | * users won't be surprised when opening the file in a text editor, but are unable to save. | ||
3174 | 3168 | */ | ||
3175 | 3169 | emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_CANT_WRITE); | ||
3176 | 3170 | } | ||
3177 | 3171 | |||
3178 | 3172 | return emblems; | ||
3179 | 3173 | } | ||
3180 | 3174 | |||
3181 | 3175 | |||
3182 | 3176 | |||
3183 | 3177 | /** | ||
3184 | 3178 | * thunar_file_set_emblem_names: | ||
3185 | 3179 | * @file : a #ThunarFile instance. | ||
3186 | 3180 | * @emblem_names : a #GList of emblem names. | ||
3187 | 3181 | * | ||
3188 | 3182 | * Sets the custom emblem name list of @file to @emblem_names | ||
3189 | 3183 | * and stores them in the @file<!---->s metadata. | ||
3190 | 3184 | **/ | ||
3191 | 3185 | void | ||
3192 | 3186 | thunar_file_set_emblem_names (ThunarFile *file, | ||
3193 | 3187 | GList *emblem_names) | ||
3194 | 3188 | { | ||
3195 | 3189 | GList *lp; | ||
3196 | 3190 | gchar **emblems = NULL; | ||
3197 | 3191 | gint n; | ||
3198 | 3192 | GFileInfo *info; | ||
3199 | 3193 | |||
3200 | 3194 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
3201 | 3195 | _thunar_return_if_fail (G_IS_FILE_INFO (file->info)); | ||
3202 | 3196 | |||
3203 | 3197 | /* allocate a zero-terminated array for the emblem names */ | ||
3204 | 3198 | emblems = g_new0 (gchar *, g_list_length (emblem_names) + 1); | ||
3205 | 3199 | |||
3206 | 3200 | /* turn the emblem_names list into a zero terminated array */ | ||
3207 | 3201 | for (lp = emblem_names, n = 0; lp != NULL; lp = lp->next) | ||
3208 | 3202 | { | ||
3209 | 3203 | /* skip special emblems */ | ||
3210 | 3204 | if (strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_SYMBOLIC_LINK) == 0 | ||
3211 | 3205 | || strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_CANT_READ) == 0 | ||
3212 | 3206 | || strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_CANT_WRITE) == 0 | ||
3213 | 3207 | || strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_DESKTOP) == 0) | ||
3214 | 3208 | continue; | ||
3215 | 3209 | |||
3216 | 3210 | /* add the emblem to our list */ | ||
3217 | 3211 | emblems[n++] = g_strdup (lp->data); | ||
3218 | 3212 | } | ||
3219 | 3213 | |||
3220 | 3214 | /* set the value in the current info */ | ||
3221 | 3215 | if (n == 0) | ||
3222 | 3216 | g_file_info_remove_attribute (file->info, "metadata::emblems"); | ||
3223 | 3217 | else | ||
3224 | 3218 | g_file_info_set_attribute_stringv (file->info, "metadata::emblems", emblems); | ||
3225 | 3219 | |||
3226 | 3220 | /* set meta data to the daemon */ | ||
3227 | 3221 | info = g_file_info_new (); | ||
3228 | 3222 | g_file_info_set_attribute_stringv (info, "metadata::emblems", emblems); | ||
3229 | 3223 | g_file_set_attributes_async (file->gfile, info, | ||
3230 | 3224 | G_FILE_QUERY_INFO_NONE, | ||
3231 | 3225 | G_PRIORITY_DEFAULT, | ||
3232 | 3226 | NULL, | ||
3233 | 3227 | thunar_file_set_emblem_names_ready, | ||
3234 | 3228 | file); | ||
3235 | 3229 | g_object_unref (G_OBJECT (info)); | ||
3236 | 3230 | |||
3237 | 3231 | g_strfreev (emblems); | ||
3238 | 3232 | } | ||
3239 | 3233 | |||
3240 | 3234 | |||
3241 | 3235 | |||
3242 | 3236 | /** | ||
3243 | 3237 | * thunar_file_set_custom_icon: | ||
3244 | 3238 | * @file : a #ThunarFile instance. | ||
3245 | 3239 | * @custom_icon : the new custom icon for the @file. | ||
3246 | 3240 | * @error : return location for errors or %NULL. | ||
3247 | 3241 | * | ||
3248 | 3242 | * Tries to change the custom icon of the .desktop file referred | ||
3249 | 3243 | * to by @file. If that fails, %FALSE is returned and the | ||
3250 | 3244 | * @error is set accordingly. | ||
3251 | 3245 | * | ||
3252 | 3246 | * Return value: %TRUE if the icon of @file was changed, %FALSE otherwise. | ||
3253 | 3247 | **/ | ||
3254 | 3248 | gboolean | ||
3255 | 3249 | thunar_file_set_custom_icon (ThunarFile *file, | ||
3256 | 3250 | const gchar *custom_icon, | ||
3257 | 3251 | GError **error) | ||
3258 | 3252 | { | ||
3259 | 3253 | GKeyFile *key_file; | ||
3260 | 3254 | |||
3261 | 3255 | _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); | ||
3262 | 3256 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); | ||
3263 | 3257 | _thunar_return_val_if_fail (custom_icon != NULL, FALSE); | ||
3264 | 3258 | |||
3265 | 3259 | key_file = thunar_g_file_query_key_file (file->gfile, NULL, error); | ||
3266 | 3260 | |||
3267 | 3261 | if (key_file == NULL) | ||
3268 | 3262 | return FALSE; | ||
3269 | 3263 | |||
3270 | 3264 | g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, | ||
3271 | 3265 | G_KEY_FILE_DESKTOP_KEY_ICON, custom_icon); | ||
3272 | 3266 | |||
3273 | 3267 | if (thunar_g_file_write_key_file (file->gfile, key_file, NULL, error)) | ||
3274 | 3268 | { | ||
3275 | 3269 | /* tell everybody that we have changed */ | ||
3276 | 3270 | thunar_file_changed (file); | ||
3277 | 3271 | |||
3278 | 3272 | g_key_file_free (key_file); | ||
3279 | 3273 | return TRUE; | ||
3280 | 3274 | } | ||
3281 | 3275 | else | ||
3282 | 3276 | { | ||
3283 | 3277 | g_key_file_free (key_file); | ||
3284 | 3278 | return FALSE; | ||
3285 | 3279 | } | ||
3286 | 3280 | } | ||
3287 | 3281 | |||
3288 | 3282 | |||
3289 | 3283 | /** | ||
3290 | 3284 | * thunar_file_is_desktop: | ||
3291 | 3285 | * @file : a #ThunarFile. | ||
3292 | 3286 | * | ||
3293 | 3287 | * Checks whether @file refers to the users desktop directory. | ||
3294 | 3288 | * | ||
3295 | 3289 | * Return value: %TRUE if @file is the users desktop directory. | ||
3296 | 3290 | **/ | ||
3297 | 3291 | gboolean | ||
3298 | 3292 | thunar_file_is_desktop (const ThunarFile *file) | ||
3299 | 3293 | { | ||
3300 | 3294 | GFile *desktop; | ||
3301 | 3295 | gboolean is_desktop = FALSE; | ||
3302 | 3296 | |||
3303 | 3297 | desktop = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)); | ||
3304 | 3298 | is_desktop = g_file_equal (file->gfile, desktop); | ||
3305 | 3299 | g_object_unref (desktop); | ||
3306 | 3300 | |||
3307 | 3301 | return is_desktop; | ||
3308 | 3302 | } | ||
3309 | 3303 | |||
3310 | 3304 | |||
3311 | 3305 | |||
3312 | 3306 | const gchar * | ||
3313 | 3307 | thunar_file_get_thumbnail_path (ThunarFile *file) | ||
3314 | 3308 | { | ||
3315 | 3309 | GChecksum *checksum; | ||
3316 | 3310 | gchar *basename; | ||
3317 | 3311 | gchar *uri; | ||
3318 | 3312 | |||
3319 | 3313 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
3320 | 3314 | |||
3321 | 3315 | /* if the thumbstate is known to be not there, return null */ | ||
3322 | 3316 | if (thunar_file_get_thumb_state (file) == THUNAR_FILE_THUMB_STATE_NONE) | ||
3323 | 3317 | return NULL; | ||
3324 | 3318 | |||
3325 | 3319 | if (G_UNLIKELY (file->thumbnail_path == NULL)) | ||
3326 | 3320 | { | ||
3327 | 3321 | checksum = g_checksum_new (G_CHECKSUM_MD5); | ||
3328 | 3322 | if (G_LIKELY (checksum != NULL)) | ||
3329 | 3323 | { | ||
3330 | 3324 | uri = thunar_file_dup_uri (file); | ||
3331 | 3325 | g_checksum_update (checksum, (const guchar *) uri, strlen (uri)); | ||
3332 | 3326 | g_free (uri); | ||
3333 | 3327 | |||
3334 | 3328 | basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); | ||
3335 | 3329 | g_checksum_free (checksum); | ||
3336 | 3330 | |||
3337 | 3331 | file->thumbnail_path = g_build_filename (xfce_get_homedir (), ".thumbnails", | ||
3338 | 3332 | "normal", basename, NULL); | ||
3339 | 3333 | |||
3340 | 3334 | g_free (basename); | ||
3341 | 3335 | } | ||
3342 | 3336 | } | ||
3343 | 3337 | |||
3344 | 3338 | return file->thumbnail_path; | ||
3345 | 3339 | } | ||
3346 | 3340 | |||
3347 | 3341 | |||
3348 | 3342 | |||
3349 | 3343 | /** | ||
3350 | 3344 | * thunar_file_get_thumb_state: | ||
3351 | 3345 | * @file : a #ThunarFile. | ||
3352 | 3346 | * | ||
3353 | 3347 | * Returns the current #ThunarFileThumbState for @file. This | ||
3354 | 3348 | * method is intended to be used by #ThunarIconFactory only. | ||
3355 | 3349 | * | ||
3356 | 3350 | * Return value: the #ThunarFileThumbState for @file. | ||
3357 | 3351 | **/ | ||
3358 | 3352 | ThunarFileThumbState | ||
3359 | 3353 | thunar_file_get_thumb_state (const ThunarFile *file) | ||
3360 | 3354 | { | ||
3361 | 3355 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), THUNAR_FILE_THUMB_STATE_UNKNOWN); | ||
3362 | 3356 | return FLAG_GET_THUMB_STATE (file); | ||
3363 | 3357 | } | ||
3364 | 3358 | |||
3365 | 3359 | |||
3366 | 3360 | |||
3367 | 3361 | /** | ||
3368 | 3362 | * thunar_file_set_thumb_state: | ||
3369 | 3363 | * @file : a #ThunarFile. | ||
3370 | 3364 | * @thumb_state : the new #ThunarFileThumbState. | ||
3371 | 3365 | * | ||
3372 | 3366 | * Sets the #ThunarFileThumbState for @file to @thumb_state. | ||
3373 | 3367 | * This will cause a "file-changed" signal to be emitted from | ||
3374 | 3368 | * #ThunarFileMonitor. | ||
3375 | 3369 | **/ | ||
3376 | 3370 | void | ||
3377 | 3371 | thunar_file_set_thumb_state (ThunarFile *file, | ||
3378 | 3372 | ThunarFileThumbState state) | ||
3379 | 3373 | { | ||
3380 | 3374 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
3381 | 3375 | |||
3382 | 3376 | /* check if the state changes */ | ||
3383 | 3377 | if (thunar_file_get_thumb_state (file) == state) | ||
3384 | 3378 | return; | ||
3385 | 3379 | |||
3386 | 3380 | /* set the new thumbnail state */ | ||
3387 | 3381 | FLAG_SET_THUMB_STATE (file, state); | ||
3388 | 3382 | |||
3389 | 3383 | /* remove path if the type is not supported */ | ||
3390 | 3384 | if (state == THUNAR_FILE_THUMB_STATE_NONE | ||
3391 | 3385 | && file->thumbnail_path != NULL) | ||
3392 | 3386 | { | ||
3393 | 3387 | g_free (file->thumbnail_path); | ||
3394 | 3388 | file->thumbnail_path = NULL; | ||
3395 | 3389 | } | ||
3396 | 3390 | |||
3397 | 3391 | /* if the file has a thumbnail, reload it */ | ||
3398 | 3392 | if (state == THUNAR_FILE_THUMB_STATE_READY) | ||
3399 | 3393 | thunar_file_monitor_file_changed (file); | ||
3400 | 3394 | } | ||
3401 | 3395 | |||
3402 | 3396 | |||
3403 | 3397 | |||
3404 | 3398 | /** | ||
3405 | 3399 | * thunar_file_get_custom_icon: | ||
3406 | 3400 | * @file : a #ThunarFile instance. | ||
3407 | 3401 | * | ||
3408 | 3402 | * Queries the custom icon from @file if any, else %NULL is returned. | ||
3409 | 3403 | * The custom icon can be either a themed icon name or an absolute path | ||
3410 | 3404 | * to an icon file in the local file system. | ||
3411 | 3405 | * | ||
3412 | 3406 | * Return value: the custom icon for @file or %NULL. | ||
3413 | 3407 | **/ | ||
3414 | 3408 | const gchar * | ||
3415 | 3409 | thunar_file_get_custom_icon (const ThunarFile *file) | ||
3416 | 3410 | { | ||
3417 | 3411 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
3418 | 3412 | return file->custom_icon_name; | ||
3419 | 3413 | } | ||
3420 | 3414 | |||
3421 | 3415 | |||
3422 | 3416 | |||
3423 | 3417 | /** | ||
3424 | 3418 | * thunar_file_get_preview_icon: | ||
3425 | 3419 | * @file : a #ThunarFile instance. | ||
3426 | 3420 | * | ||
3427 | 3421 | * Returns the preview icon for @file if any, else %NULL is returned. | ||
3428 | 3422 | * | ||
3429 | 3423 | * Return value: the custom icon for @file or %NULL, the GIcon is owner | ||
3430 | 3424 | * by the file, so do not unref it. | ||
3431 | 3425 | **/ | ||
3432 | 3426 | GIcon * | ||
3433 | 3427 | thunar_file_get_preview_icon (const ThunarFile *file) | ||
3434 | 3428 | { | ||
3435 | 3429 | GObject *icon; | ||
3436 | 3430 | |||
3437 | 3431 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
3438 | 3432 | _thunar_return_val_if_fail (G_IS_FILE_INFO (file->info), NULL); | ||
3439 | 3433 | |||
3440 | 3434 | icon = g_file_info_get_attribute_object (file->info, G_FILE_ATTRIBUTE_PREVIEW_ICON); | ||
3441 | 3435 | if (G_LIKELY (icon != NULL)) | ||
3442 | 3436 | return G_ICON (icon); | ||
3443 | 3437 | |||
3444 | 3438 | return NULL; | ||
3445 | 3439 | } | ||
3446 | 3440 | |||
3447 | 3441 | |||
3448 | 3442 | |||
3449 | 3443 | GFilesystemPreviewType | ||
3450 | 3444 | thunar_file_get_preview_type (const ThunarFile *file) | ||
3451 | 3445 | { | ||
3452 | 3446 | GFilesystemPreviewType preview; | ||
3453 | 3447 | GFileInfo *info; | ||
3454 | 3448 | |||
3455 | 3449 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), G_FILESYSTEM_PREVIEW_TYPE_NEVER); | ||
3456 | 3450 | _thunar_return_val_if_fail (G_IS_FILE (file->gfile), G_FILESYSTEM_PREVIEW_TYPE_NEVER); | ||
3457 | 3451 | |||
3458 | 3452 | info = g_file_query_filesystem_info (file->gfile, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, NULL, NULL); | ||
3459 | 3453 | if (G_LIKELY (info != NULL)) | ||
3460 | 3454 | { | ||
3461 | 3455 | preview = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW); | ||
3462 | 3456 | g_object_unref (G_OBJECT (info)); | ||
3463 | 3457 | } | ||
3464 | 3458 | else | ||
3465 | 3459 | { | ||
3466 | 3460 | /* assume we don't know */ | ||
3467 | 3461 | preview = G_FILESYSTEM_PREVIEW_TYPE_NEVER; | ||
3468 | 3462 | } | ||
3469 | 3463 | |||
3470 | 3464 | return preview; | ||
3471 | 3465 | } | ||
3472 | 3466 | |||
3473 | 3467 | |||
3474 | 3468 | |||
3475 | 3469 | static const gchar * | ||
3476 | 3470 | thunar_file_get_icon_name_for_state (const gchar *icon_name, | ||
3477 | 3471 | ThunarFileIconState icon_state) | ||
3478 | 3472 | { | ||
3479 | 3473 | if (exo_str_is_empty (icon_name)) | ||
3480 | 3474 | return NULL; | ||
3481 | 3475 | |||
3482 | 3476 | /* check if we have an accept icon for the icon we found */ | ||
3483 | 3477 | if (icon_state != THUNAR_FILE_ICON_STATE_DEFAULT | ||
3484 | 3478 | && (strcmp (icon_name, "inode-directory") == 0 | ||
3485 | 3479 | || strcmp (icon_name, "folder") == 0)) | ||
3486 | 3480 | { | ||
3487 | 3481 | if (icon_state == THUNAR_FILE_ICON_STATE_DROP) | ||
3488 | 3482 | return "folder-drag-accept"; | ||
3489 | 3483 | else if (icon_state == THUNAR_FILE_ICON_STATE_OPEN) | ||
3490 | 3484 | return "folder-open"; | ||
3491 | 3485 | } | ||
3492 | 3486 | |||
3493 | 3487 | return icon_name; | ||
3494 | 3488 | } | ||
3495 | 3489 | |||
3496 | 3490 | |||
3497 | 3491 | |||
3498 | 3492 | /** | ||
3499 | 3493 | * thunar_file_get_icon_name: | ||
3500 | 3494 | * @file : a #ThunarFile instance. | ||
3501 | 3495 | * @icon_state : the state of the @file<!---->s icon we are interested in. | ||
3502 | 3496 | * @icon_theme : the #GtkIconTheme on which to lookup up the icon name. | ||
3503 | 3497 | * | ||
3504 | 3498 | * Returns the name of the icon that can be used to present @file, based | ||
3505 | 3499 | * on the given @icon_state and @icon_theme. | ||
3506 | 3500 | * | ||
3507 | 3501 | * Return value: the icon name for @file in @icon_theme. | ||
3508 | 3502 | **/ | ||
3509 | 3503 | const gchar * | ||
3510 | 3504 | thunar_file_get_icon_name (ThunarFile *file, | ||
3511 | 3505 | ThunarFileIconState icon_state, | ||
3512 | 3506 | GtkIconTheme *icon_theme) | ||
3513 | 3507 | { | ||
3514 | 3508 | GFile *icon_file; | ||
3515 | 3509 | GIcon *icon = NULL; | ||
3516 | 3510 | const gchar * const *names; | ||
3517 | 3511 | gchar *icon_name = NULL; | ||
3518 | 3512 | gchar *path; | ||
3519 | 3513 | const gchar *special_names[] = { NULL, "folder", NULL }; | ||
3520 | 3514 | guint i; | ||
3521 | 3515 | const gchar *special_dir; | ||
3522 | 3516 | GFileInfo *fileinfo; | ||
3523 | 3517 | |||
3524 | 3518 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); | ||
3525 | 3519 | _thunar_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL); | ||
3526 | 3520 | |||
3527 | 3521 | /* return cached name */ | ||
3528 | 3522 | if (G_LIKELY (file->icon_name != NULL)) | ||
3529 | 3523 | return thunar_file_get_icon_name_for_state (file->icon_name, icon_state); | ||
3530 | 3524 | |||
3531 | 3525 | /* the system root folder has a special icon */ | ||
3532 | 3526 | if (thunar_file_is_directory (file)) | ||
3533 | 3527 | { | ||
3534 | 3528 | if (G_LIKELY (thunar_file_is_local (file))) | ||
3535 | 3529 | { | ||
3536 | 3530 | path = g_file_get_path (file->gfile); | ||
3537 | 3531 | if (G_LIKELY (path != NULL)) | ||
3538 | 3532 | { | ||
3539 | 3533 | if (strcmp (path, G_DIR_SEPARATOR_S) == 0) | ||
3540 | 3534 | *special_names = "drive-harddisk"; | ||
3541 | 3535 | else if (strcmp (path, xfce_get_homedir ()) == 0) | ||
3542 | 3536 | *special_names = "user-home"; | ||
3543 | 3537 | else | ||
3544 | 3538 | { | ||
3545 | 3539 | for (i = 0; i < G_N_ELEMENTS (thunar_file_dirs); i++) | ||
3546 | 3540 | { | ||
3547 | 3541 | special_dir = g_get_user_special_dir (thunar_file_dirs[i].type); | ||
3548 | 3542 | if (special_dir != NULL | ||
3549 | 3543 | && strcmp (path, special_dir) == 0) | ||
3550 | 3544 | { | ||
3551 | 3545 | *special_names = thunar_file_dirs[i].icon_name; | ||
3552 | 3546 | break; | ||
3553 | 3547 | } | ||
3554 | 3548 | } | ||
3555 | 3549 | } | ||
3556 | 3550 | |||
3557 | 3551 | g_free (path); | ||
3558 | 3552 | } | ||
3559 | 3553 | } | ||
3560 | 3554 | else if (!thunar_file_has_parent (file)) | ||
3561 | 3555 | { | ||
3562 | 3556 | if (g_file_has_uri_scheme (file->gfile, "trash")) | ||
3563 | 3557 | { | ||
3564 | 3558 | special_names[0] = thunar_file_get_item_count (file) > 0 ? "user-trash-full" : "user-trash"; | ||
3565 | 3559 | special_names[1] = "user-trash"; | ||
3566 | 3560 | } | ||
3567 | 3561 | else if (g_file_has_uri_scheme (file->gfile, "recent")) | ||
3568 | 3562 | { | ||
3569 | 3563 | special_names[0] = "document-open-recent"; | ||
3570 | 3564 | } | ||
3571 | 3565 | else if (g_file_has_uri_scheme (file->gfile, "computer")) | ||
3572 | 3566 | { | ||
3573 | 3567 | special_names[0] = "computer"; | ||
3574 | 3568 | } | ||
3575 | 3569 | } | ||
3576 | 3570 | |||
3577 | 3571 | if (*special_names != NULL) | ||
3578 | 3572 | { | ||
3579 | 3573 | names = special_names; | ||
3580 | 3574 | goto check_names; | ||
3581 | 3575 | } | ||
3582 | 3576 | } | ||
3583 | 3577 | else if (thunar_file_is_mountable (file)) | ||
3584 | 3578 | { | ||
3585 | 3579 | /* query the icon (computer:// backend) */ | ||
3586 | 3580 | fileinfo = g_file_query_info (file->gfile, | ||
3587 | 3581 | G_FILE_ATTRIBUTE_STANDARD_ICON, | ||
3588 | 3582 | G_FILE_QUERY_INFO_NONE, NULL, NULL); | ||
3589 | 3583 | if (G_LIKELY (fileinfo != NULL)) | ||
3590 | 3584 | { | ||
3591 | 3585 | /* take the icon from the info */ | ||
3592 | 3586 | icon = g_file_info_get_icon (fileinfo); | ||
3593 | 3587 | if (G_LIKELY (icon != NULL)) | ||
3594 | 3588 | g_object_ref (icon); | ||
3595 | 3589 | |||
3596 | 3590 | /* release */ | ||
3597 | 3591 | g_object_unref (G_OBJECT (fileinfo)); | ||
3598 | 3592 | |||
3599 | 3593 | if (G_LIKELY (icon != NULL)) | ||
3600 | 3594 | goto check_icon; | ||
3601 | 3595 | } | ||
3602 | 3596 | } | ||
3603 | 3597 | |||
3604 | 3598 | /* try again later */ | ||
3605 | 3599 | if (file->info == NULL) | ||
3606 | 3600 | return NULL; | ||
3607 | 3601 | |||
3608 | 3602 | /* lookup for content type, just like gio does for local files */ | ||
3609 | 3603 | icon = g_content_type_get_icon (thunar_file_get_content_type (file)); | ||
3610 | 3604 | if (G_LIKELY (icon != NULL)) | ||
3611 | 3605 | { | ||
3612 | 3606 | check_icon: | ||
3613 | 3607 | |||
3614 | 3608 | if (G_IS_THEMED_ICON (icon)) | ||
3615 | 3609 | { | ||
3616 | 3610 | names = g_themed_icon_get_names (G_THEMED_ICON (icon)); | ||
3617 | 3611 | |||
3618 | 3612 | check_names: | ||
3619 | 3613 | |||
3620 | 3614 | if (G_LIKELY (names != NULL)) | ||
3621 | 3615 | { | ||
3622 | 3616 | for (i = 0; names[i] != NULL; ++i) | ||
3623 | 3617 | if (*names[i] != '(' /* see gnome bug 688042 */ | ||
3624 | 3618 | && gtk_icon_theme_has_icon (icon_theme, names[i])) | ||
3625 | 3619 | { | ||
3626 | 3620 | icon_name = g_strdup (names[i]); | ||
3627 | 3621 | break; | ||
3628 | 3622 | } | ||
3629 | 3623 | } | ||
3630 | 3624 | } | ||
3631 | 3625 | else if (G_IS_FILE_ICON (icon)) | ||
3632 | 3626 | { | ||
3633 | 3627 | icon_file = g_file_icon_get_file (G_FILE_ICON (icon)); | ||
3634 | 3628 | if (icon_file != NULL) | ||
3635 | 3629 | icon_name = g_file_get_path (icon_file); | ||
3636 | 3630 | } | ||
3637 | 3631 | |||
3638 | 3632 | if (G_LIKELY (icon != NULL)) | ||
3639 | 3633 | g_object_unref (icon); | ||
3640 | 3634 | } | ||
3641 | 3635 | |||
3642 | 3636 | /* store new name, or empty string to avoid recursion */ | ||
3643 | 3637 | if (G_LIKELY (icon_name != NULL)) | ||
3644 | 3638 | file->icon_name = icon_name; | ||
3645 | 3639 | else | ||
3646 | 3640 | file->icon_name = g_strdup (""); | ||
3647 | 3641 | |||
3648 | 3642 | return thunar_file_get_icon_name_for_state (file->icon_name, icon_state); | ||
3649 | 3643 | } | ||
3650 | 3644 | |||
3651 | 3645 | |||
3652 | 3646 | |||
3653 | 3647 | /** | ||
3654 | 3648 | * thunar_file_watch: | ||
3655 | 3649 | * @file : a #ThunarFile instance. | ||
3656 | 3650 | * | ||
3657 | 3651 | * Tells @file to watch itself for changes. Not all #ThunarFile | ||
3658 | 3652 | * implementations must support this, but if a #ThunarFile | ||
3659 | 3653 | * implementation implements the thunar_file_watch() method, | ||
3660 | 3654 | * it must also implement the thunar_file_unwatch() method. | ||
3661 | 3655 | * | ||
3662 | 3656 | * The #ThunarFile base class implements automatic "ref | ||
3663 | 3657 | * counting" for watches, that says, you can call thunar_file_watch() | ||
3664 | 3658 | * multiple times, but the virtual method will be invoked only | ||
3665 | 3659 | * once. This also means that you MUST call thunar_file_unwatch() | ||
3666 | 3660 | * for every thunar_file_watch() invokation, else the application | ||
3667 | 3661 | * will abort. | ||
3668 | 3662 | **/ | ||
3669 | 3663 | void | ||
3670 | 3664 | thunar_file_watch (ThunarFile *file) | ||
3671 | 3665 | { | ||
3672 | 3666 | ThunarFileWatch *file_watch; | ||
3673 | 3667 | |||
3674 | 3668 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
3675 | 3669 | |||
3676 | 3670 | file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark); | ||
3677 | 3671 | if (file_watch == NULL) | ||
3678 | 3672 | { | ||
3679 | 3673 | file_watch = g_slice_new (ThunarFileWatch); | ||
3680 | 3674 | file_watch->watch_count = 1; | ||
3681 | 3675 | |||
3682 | 3676 | /* create a file or directory monitor */ | ||
3683 | 3677 | file_watch->monitor = g_file_monitor (file->gfile, G_FILE_MONITOR_WATCH_MOUNTS, NULL, NULL); | ||
3684 | 3678 | if (G_LIKELY (file_watch->monitor != NULL)) | ||
3685 | 3679 | { | ||
3686 | 3680 | /* watch monitor for file changes */ | ||
3687 | 3681 | g_signal_connect (file_watch->monitor, "changed", G_CALLBACK (thunar_file_monitor), file); | ||
3688 | 3682 | } | ||
3689 | 3683 | |||
3690 | 3684 | /* attach to file */ | ||
3691 | 3685 | g_object_set_qdata_full (G_OBJECT (file), thunar_file_watch_quark, file_watch, thunar_file_watch_destroyed); | ||
3692 | 3686 | } | ||
3693 | 3687 | else | ||
3694 | 3688 | { | ||
3695 | 3689 | /* increase watch count */ | ||
3696 | 3690 | _thunar_return_if_fail (G_IS_FILE_MONITOR (file_watch->monitor)); | ||
3697 | 3691 | file_watch->watch_count++; | ||
3698 | 3692 | } | ||
3699 | 3693 | } | ||
3700 | 3694 | |||
3701 | 3695 | |||
3702 | 3696 | |||
3703 | 3697 | /** | ||
3704 | 3698 | * thunar_file_unwatch: | ||
3705 | 3699 | * @file : a #ThunarFile instance. | ||
3706 | 3700 | * | ||
3707 | 3701 | * See thunar_file_watch() for a description of how watching | ||
3708 | 3702 | * #ThunarFile<!---->s works. | ||
3709 | 3703 | **/ | ||
3710 | 3704 | void | ||
3711 | 3705 | thunar_file_unwatch (ThunarFile *file) | ||
3712 | 3706 | { | ||
3713 | 3707 | ThunarFileWatch *file_watch; | ||
3714 | 3708 | |||
3715 | 3709 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
3716 | 3710 | |||
3717 | 3711 | file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark); | ||
3718 | 3712 | if (file_watch != NULL) | ||
3719 | 3713 | { | ||
3720 | 3714 | /* remove if this was the last ref */ | ||
3721 | 3715 | if (--file_watch->watch_count == 0) | ||
3722 | 3716 | g_object_set_qdata (G_OBJECT (file), thunar_file_watch_quark, NULL); | ||
3723 | 3717 | } | ||
3724 | 3718 | else | ||
3725 | 3719 | { | ||
3726 | 3720 | _thunar_assert_not_reached (); | ||
3727 | 3721 | } | ||
3728 | 3722 | } | ||
3729 | 3723 | |||
3730 | 3724 | |||
3731 | 3725 | |||
3732 | 3726 | /** | ||
3733 | 3727 | * thunar_file_reload: | ||
3734 | 3728 | * @file : a #ThunarFile instance. | ||
3735 | 3729 | * | ||
3736 | 3730 | * Tells @file to reload its internal state, e.g. by reacquiring | ||
3737 | 3731 | * the file info from the underlying media. | ||
3738 | 3732 | * | ||
3739 | 3733 | * You must be able to handle the case that @file is | ||
3740 | 3734 | * destroyed during the reload call. | ||
3741 | 3735 | **/ | ||
3742 | 3736 | void | ||
3743 | 3737 | thunar_file_reload (ThunarFile *file) | ||
3744 | 3738 | { | ||
3745 | 3739 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
3746 | 3740 | |||
3747 | 3741 | /* clear file pxmap cache */ | ||
3748 | 3742 | thunar_icon_factory_clear_pixmap_cache (file); | ||
3749 | 3743 | |||
3750 | 3744 | if (!thunar_file_load (file, NULL, NULL)) | ||
3751 | 3745 | { | ||
3752 | 3746 | /* destroy the file if we cannot query any file information */ | ||
3753 | 3747 | thunar_file_destroy (file); | ||
3754 | 3748 | return; | ||
3755 | 3749 | } | ||
3756 | 3750 | |||
3757 | 3751 | /* ... and tell others */ | ||
3758 | 3752 | thunar_file_changed (file); | ||
3759 | 3753 | |||
3760 | 3754 | } | ||
3761 | 3755 | |||
3762 | 3756 | |||
3763 | 3757 | |||
3764 | 3758 | /** | ||
3765 | 3759 | * thunar_file_destroy: | ||
3766 | 3760 | * @file : a #ThunarFile instance. | ||
3767 | 3761 | * | ||
3768 | 3762 | * Emits the ::destroy signal notifying all reference holders | ||
3769 | 3763 | * that they should release their references to the @file. | ||
3770 | 3764 | * | ||
3771 | 3765 | * This method is very similar to what gtk_object_destroy() | ||
3772 | 3766 | * does for #GtkObject<!---->s. | ||
3773 | 3767 | **/ | ||
3774 | 3768 | void | ||
3775 | 3769 | thunar_file_destroy (ThunarFile *file) | ||
3776 | 3770 | { | ||
3777 | 3771 | _thunar_return_if_fail (THUNAR_IS_FILE (file)); | ||
3778 | 3772 | |||
3779 | 3773 | if (!FLAG_IS_SET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION)) | ||
3780 | 3774 | { | ||
3781 | 3775 | /* take an additional reference on the file, as the file-destroyed | ||
3782 | 3776 | * invocation may already release the last reference. | ||
3783 | 3777 | */ | ||
3784 | 3778 | g_object_ref (G_OBJECT (file)); | ||
3785 | 3779 | |||
3786 | 3780 | /* tell the file monitor that this file was destroyed */ | ||
3787 | 3781 | thunar_file_monitor_file_destroyed (file); | ||
3788 | 3782 | |||
3789 | 3783 | /* run the dispose handler */ | ||
3790 | 3784 | g_object_run_dispose (G_OBJECT (file)); | ||
3791 | 3785 | |||
3792 | 3786 | /* release our reference */ | ||
3793 | 3787 | g_object_unref (G_OBJECT (file)); | ||
3794 | 3788 | } | ||
3795 | 3789 | } | ||
3796 | 3790 | |||
3797 | 3791 | |||
3798 | 3792 | |||
3799 | 3793 | /** | ||
3800 | 3794 | * thunar_file_compare_by_name: | ||
3801 | 3795 | * @file_a : the first #ThunarFile. | ||
3802 | 3796 | * @file_b : the second #ThunarFile. | ||
3803 | 3797 | * @case_sensitive : whether the comparison should be case-sensitive. | ||
3804 | 3798 | * | ||
3805 | 3799 | * Compares @file_a and @file_b by their display names. If @case_sensitive | ||
3806 | 3800 | * is %TRUE the comparison will be case-sensitive. | ||
3807 | 3801 | * | ||
3808 | 3802 | * Return value: -1 if @file_a should be sorted before @file_b, 1 if | ||
3809 | 3803 | * @file_b should be sorted before @file_a, 0 if equal. | ||
3810 | 3804 | **/ | ||
3811 | 3805 | gint | ||
3812 | 3806 | thunar_file_compare_by_name (const ThunarFile *file_a, | ||
3813 | 3807 | const ThunarFile *file_b, | ||
3814 | 3808 | gboolean case_sensitive) | ||
3815 | 3809 | { | ||
3816 | 3810 | gint result = 0; | ||
3817 | 3811 | |||
3818 | 3812 | #ifdef G_ENABLE_DEBUG | ||
3819 | 3813 | /* probably too expensive to do the instance check every time | ||
3820 | 3814 | * this function is called, so only for debugging builds. | ||
3821 | 3815 | */ | ||
3822 | 3816 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file_a), 0); | ||
3823 | 3817 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file_b), 0); | ||
3824 | 3818 | #endif | ||
3825 | 3819 | |||
3826 | 3820 | /* case insensitive checking */ | ||
3827 | 3821 | if (G_LIKELY (!case_sensitive)) | ||
3828 | 3822 | result = strcmp (file_a->collate_key_nocase, file_b->collate_key_nocase); | ||
3829 | 3823 | |||
3830 | 3824 | /* fall-back to case sensitive */ | ||
3831 | 3825 | if (result == 0) | ||
3832 | 3826 | result = strcmp (file_a->collate_key, file_b->collate_key); | ||
3833 | 3827 | |||
3834 | 3828 | /* this happens in the trash */ | ||
3835 | 3829 | if (result == 0) | ||
3836 | 3830 | { | ||
3837 | 3831 | result = g_strcmp0 (thunar_file_get_original_path (file_a), | ||
3838 | 3832 | thunar_file_get_original_path (file_b)); | ||
3839 | 3833 | } | ||
3840 | 3834 | |||
3841 | 3835 | return result; | ||
3842 | 3836 | } | ||
3843 | 3837 | |||
3844 | 3838 | |||
3845 | 3839 | |||
3846 | 3840 | static gboolean | ||
3847 | 3841 | thunar_file_same_filesystem (const ThunarFile *file_a, | ||
3848 | 3842 | const ThunarFile *file_b) | ||
3849 | 3843 | { | ||
3850 | 3844 | const gchar *filesystem_id_a; | ||
3851 | 3845 | const gchar *filesystem_id_b; | ||
3852 | 3846 | |||
3853 | 3847 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file_a), FALSE); | ||
3854 | 3848 | _thunar_return_val_if_fail (THUNAR_IS_FILE (file_b), FALSE); | ||
3855 | 3849 | |||
3856 | 3850 | /* return false if we have no information about one of the files */ | ||
3857 | 3851 | if (file_a->info == NULL || file_b->info == NULL) | ||
3858 | 3852 | return FALSE; | ||
3859 | 3853 | |||
3860 | 3854 | /* determine the filesystem IDs */ | ||
3861 | 3855 | filesystem_id_a = g_file_info_get_attribute_string (file_a->info, | ||
3862 | 3856 | G_FILE_ATTRIBUTE_ID_FILESYSTEM); | ||
3863 | 3857 | |||
3864 | 3858 | filesystem_id_b = g_file_info_get_attribute_string (file_b->info, | ||
3865 | 3859 | G_FILE_ATTRIBUTE_ID_FILESYSTEM); | ||
3866 | 3860 | |||
3867 | 3861 | /* compare the filesystem IDs */ | ||
3868 | 3862 | return exo_str_is_equal (filesystem_id_a, filesystem_id_b); | ||
3869 | 3863 | } | ||
3870 | 3864 | |||
3871 | 3865 | |||
3872 | 3866 | |||
3873 | 3867 | /** | ||
3874 | 3868 | * thunar_file_cache_lookup: | ||
3875 | 3869 | * @file : a #GFile. | ||
3876 | 3870 | * | ||
3877 | 3871 | * Looks up the #ThunarFile for @file in the internal file | ||
3878 | 3872 | * cache and returns the file present for @file in the | ||
3879 | 3873 | * cache or %NULL if no #ThunarFile is cached for @file. | ||
3880 | 3874 | * | ||
3881 | 3875 | * Note that no reference is taken for the caller. | ||
3882 | 3876 | * | ||
3883 | 3877 | * This method should not be used but in very rare cases. | ||
3884 | 3878 | * Consider using thunar_file_get() instead. | ||
3885 | 3879 | * | ||
3886 | 3880 | * Return value: the #ThunarFile for @file in the internal | ||
3887 | 3881 | * cache, or %NULL. | ||
3888 | 3882 | **/ | ||
3889 | 3883 | ThunarFile * | ||
3890 | 3884 | thunar_file_cache_lookup (const GFile *file) | ||
3891 | 3885 | { | ||
3892 | 3886 | ThunarFile *cached_file; | ||
3893 | 3887 | |||
3894 | 3888 | _thunar_return_val_if_fail (G_IS_FILE (file), NULL); | ||
3895 | 3889 | |||
3896 | 3890 | G_LOCK (file_cache_mutex); | ||
3897 | 3891 | |||
3898 | 3892 | /* allocate the ThunarFile cache on-demand */ | ||
3899 | 3893 | if (G_UNLIKELY (file_cache == NULL)) | ||
3900 | 3894 | { | ||
3901 | 3895 | file_cache = g_hash_table_new_full (g_file_hash, | ||
3902 | 3896 | (GEqualFunc) g_file_equal, | ||
3903 | 3897 | (GDestroyNotify) g_object_unref, | ||
3904 | 3898 | NULL); | ||
3905 | 3899 | } | ||
3906 | 3900 | |||
3907 | 3901 | cached_file = g_hash_table_lookup (file_cache, file); | ||
3908 | 3902 | |||
3909 | 3903 | G_UNLOCK (file_cache_mutex); | ||
3910 | 3904 | |||
3911 | 3905 | return cached_file; | ||
3912 | 3906 | } | ||
3913 | 3907 | |||
3914 | 3908 | |||
3915 | 3909 | |||
3916 | 3910 | gchar * | ||
3917 | 3911 | thunar_file_cached_display_name (const GFile *file) | ||
3918 | 3912 | { | ||
3919 | 3913 | ThunarFile *cached_file; | ||
3920 | 3914 | gchar *display_name; | ||
3921 | 3915 | |||
3922 | 3916 | /* check if we have a ThunarFile for it in the cache (usually is the case) */ | ||
3923 | 3917 | cached_file = thunar_file_cache_lookup (file); | ||
3924 | 3918 | if (cached_file != NULL) | ||
3925 | 3919 | { | ||
3926 | 3920 | /* determine the display name of the file */ | ||
3927 | 3921 | display_name = g_strdup (thunar_file_get_display_name (cached_file)); | ||
3928 | 3922 | } | ||
3929 | 3923 | else | ||
3930 | 3924 | { | ||
3931 | 3925 | /* determine something a hopefully good approximation of the display name */ | ||
3932 | 3926 | display_name = thunar_g_file_get_display_name (G_FILE (file)); | ||
3933 | 3927 | } | ||
3934 | 3928 | |||
3935 | 3929 | return display_name; | ||
3936 | 3930 | } | ||
3937 | 3931 | |||
3938 | 3932 | |||
3939 | 3933 | |||
3940 | 3934 | static gint | ||
3941 | 3935 | compare_app_infos (gconstpointer a, | ||
3942 | 3936 | gconstpointer b) | ||
3943 | 3937 | { | ||
3944 | 3938 | return g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b)) ? 0 : 1; | ||
3945 | 3939 | } | ||
3946 | 3940 | |||
3947 | 3941 | |||
3948 | 3942 | |||
3949 | 3943 | /** | ||
3950 | 3944 | * thunar_file_list_get_applications: | ||
3951 | 3945 | * @file_list : a #GList of #ThunarFile<!---->s. | ||
3952 | 3946 | * | ||
3953 | 3947 | * Returns the #GList of #GAppInfo<!---->s that can be used to open | ||
3954 | 3948 | * all #ThunarFile<!---->s in the given @file_list. | ||
3955 | 3949 | * | ||
3956 | 3950 | * The caller is responsible to free the returned list using something like: | ||
3957 | 3951 | * <informalexample><programlisting> | ||
3958 | 3952 | * g_list_free_full (list, g_object_unref); | ||
3959 | 3953 | * </programlisting></informalexample> | ||
3960 | 3954 | * | ||
3961 | 3955 | * Return value: the list of #GAppInfo<!---->s that can be used to open all | ||
3962 | 3956 | * items in the @file_list. | ||
3963 | 3957 | **/ | ||
3964 | 3958 | GList* | ||
3965 | 3959 | thunar_file_list_get_applications (GList *file_list) | ||
3966 | 3960 | { | ||
3967 | 3961 | GList *applications = NULL; | ||
3968 | 3962 | GList *list; | ||
3969 | 3963 | GList *next; | ||
3970 | 3964 | GList *ap; | ||
3971 | 3965 | GList *lp; | ||
3972 | 3966 | const gchar *previous_type = NULL; | ||
3973 | 3967 | const gchar *current_type; | ||
3974 | 3968 | |||
3975 | 3969 | /* determine the set of applications that can open all files */ | ||
3976 | 3970 | for (lp = file_list; lp != NULL; lp = lp->next) | ||
3977 | 3971 | { | ||
3978 | 3972 | current_type = thunar_file_get_content_type (lp->data); | ||
3979 | 3973 | |||
3980 | 3974 | /* no need to check anything if this file has the same mimetype as the previous file */ | ||
3981 | 3975 | if (current_type != NULL && previous_type != NULL) | ||
3982 | 3976 | if (G_LIKELY (g_content_type_equals (previous_type, current_type))) | ||
3983 | 3977 | continue; | ||
3984 | 3978 | |||
3985 | 3979 | /* store the previous type */ | ||
3986 | 3980 | previous_type = current_type; | ||
3987 | 3981 | |||
3988 | 3982 | /* determine the list of applications that can open this file */ | ||
3989 | 3983 | if (G_UNLIKELY (current_type != NULL)) | ||
3990 | 3984 | list = g_app_info_get_all_for_type (current_type); | ||
3991 | 3985 | else | ||
3992 | 3986 | list = NULL; | ||
3993 | 3987 | |||
3994 | 3988 | if (G_UNLIKELY (applications == NULL)) | ||
3995 | 3989 | { | ||
3996 | 3990 | /* first file, so just use the applications list */ | ||
3997 | 3991 | applications = list; | ||
3998 | 3992 | } | ||
3999 | 3993 | else | ||
4000 | 3994 | { | ||
4001 | 3995 | /* keep only the applications that are also present in list */ | ||
4002 | 3996 | for (ap = applications; ap != NULL; ap = next) | ||
4003 | 3997 | { | ||
4004 | 3998 | /* grab a pointer on the next application */ | ||
4005 | 3999 | next = ap->next; | ||
4006 | 4000 | |||
4007 | 4001 | /* check if the application is present in list */ | ||
4008 | 4002 | if (g_list_find_custom (list, ap->data, compare_app_infos) == NULL) | ||
4009 | 4003 | { | ||
4010 | 4004 | /* drop our reference on the application */ | ||
4011 | 4005 | g_object_unref (G_OBJECT (ap->data)); | ||
4012 | 4006 | |||
4013 | 4007 | /* drop this application from the list */ | ||
4014 | 4008 | applications = g_list_delete_link (applications, ap); | ||
4015 | 4009 | } | ||
4016 | 4010 | } | ||
4017 | 4011 | |||
4018 | 4012 | /* release the list of applications for this file */ | ||
4019 | 4013 | g_list_free_full (list, g_object_unref); | ||
4020 | 4014 | } | ||
4021 | 4015 | |||
4022 | 4016 | /* check if the set is still not empty */ | ||
4023 | 4017 | if (G_LIKELY (applications == NULL)) | ||
4024 | 4018 | break; | ||
4025 | 4019 | } | ||
4026 | 4020 | |||
4027 | 4021 | /* remove hidden applications */ | ||
4028 | 4022 | for (ap = applications; ap != NULL; ap = next) | ||
4029 | 4023 | { | ||
4030 | 4024 | /* grab a pointer on the next application */ | ||
4031 | 4025 | next = ap->next; | ||
4032 | 4026 | |||
4033 | 4027 | if (!thunar_g_app_info_should_show (ap->data)) | ||
4034 | 4028 | { | ||
4035 | 4029 | /* drop our reference on the application */ | ||
4036 | 4030 | g_object_unref (G_OBJECT (ap->data)); | ||
4037 | 4031 | |||
4038 | 4032 | /* drop this application from the list */ | ||
4039 | 4033 | applications = g_list_delete_link (applications, ap); | ||
4040 | 4034 | } | ||
4041 | 4035 | } | ||
4042 | 4036 | |||
4043 | 4037 | return applications; | ||
4044 | 4038 | } | ||
4045 | 4039 | |||
4046 | 4040 | |||
4047 | 4041 | |||
4048 | 4042 | /** | ||
4049 | 4043 | * thunar_file_list_to_thunar_g_file_list: | ||
4050 | 4044 | * @file_list : a #GList of #ThunarFile<!---->s. | ||
4051 | 4045 | * | ||
4052 | 4046 | * Transforms the @file_list to a #GList of #GFile<!---->s for | ||
4053 | 4047 | * the #ThunarFile<!---->s contained within @file_list. | ||
4054 | 4048 | * | ||
4055 | 4049 | * The caller is responsible to free the returned list using | ||
4056 | 4050 | * thunar_g_file_list_free() when no longer needed. | ||
4057 | 4051 | * | ||
4058 | 4052 | * Return value: the list of #GFile<!---->s for @file_list. | ||
4059 | 4053 | **/ | ||
4060 | 4054 | GList* | ||
4061 | 4055 | thunar_file_list_to_thunar_g_file_list (GList *file_list) | ||
4062 | 4056 | { | ||
4063 | 4057 | GList *list = NULL; | ||
4064 | 4058 | GList *lp; | ||
4065 | 4059 | |||
4066 | 4060 | for (lp = g_list_last (file_list); lp != NULL; lp = lp->prev) | ||
4067 | 4061 | list = g_list_prepend (list, g_object_ref (THUNAR_FILE (lp->data)->gfile)); | ||
4068 | 4062 | |||
4069 | 4063 | return list; | ||
4070 | 4064 | } | ||
4071 | 4065 | 0 | ||
4072 | === modified file '.pc/applied-patches' | |||
4073 | --- .pc/applied-patches 2014-10-31 07:27:27 +0000 | |||
4074 | +++ .pc/applied-patches 2015-01-06 18:59:01 +0000 | |||
4075 | @@ -1,7 +1,2 @@ | |||
4076 | 1 | 01_support-non-multiarch-modules.patch | 1 | 01_support-non-multiarch-modules.patch |
4077 | 2 | 02_fix-default-application-selection.patch | ||
4078 | 3 | git-xfdesktop-4.11.patch | ||
4079 | 4 | gtk3-bookmarks.patch | ||
4080 | 5 | git-save-keyboard-shortcuts.patch | ||
4081 | 6 | menu-icon-tweaks.patch | 2 | menu-icon-tweaks.patch |
4082 | 7 | git-force-toolbr-icons.patch | ||
4083 | 8 | 3 | ||
4084 | === removed directory '.pc/git-force-toolbr-icons.patch' | |||
4085 | === removed directory '.pc/git-force-toolbr-icons.patch/thunar' | |||
4086 | === removed file '.pc/git-force-toolbr-icons.patch/thunar/thunar-window.c' | |||
4087 | --- .pc/git-force-toolbr-icons.patch/thunar/thunar-window.c 2014-04-05 08:22:40 +0000 | |||
4088 | +++ .pc/git-force-toolbr-icons.patch/thunar/thunar-window.c 1970-01-01 00:00:00 +0000 | |||
4089 | @@ -1,3846 +0,0 @@ | |||
4090 | 1 | /* vi:set et ai sw=2 sts=2 ts=2: */ | ||
4091 | 2 | /*- | ||
4092 | 3 | * Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org> | ||
4093 | 4 | * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> | ||
4094 | 5 | * | ||
4095 | 6 | * This program is free software; you can redistribute it and/or | ||
4096 | 7 | * modify it under the terms of the GNU General Public License as | ||
4097 | 8 | * published by the Free Software Foundation; either version 2 of | ||
4098 | 9 | * the License, or (at your option) any later version. | ||
4099 | 10 | * | ||
4100 | 11 | * This program is distributed in the hope that it will be useful, | ||
4101 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4102 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4103 | 14 | * GNU General Public License for more details. | ||
4104 | 15 | * | ||
4105 | 16 | * You should have received a copy of the GNU General Public | ||
4106 | 17 | * License along with this program; if not, write to the Free | ||
4107 | 18 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
4108 | 19 | * Boston, MA 02110-1301, USA. | ||
4109 | 20 | */ | ||
4110 | 21 | |||
4111 | 22 | #ifdef HAVE_CONFIG_H | ||
4112 | 23 | #include <config.h> | ||
4113 | 24 | #endif | ||
4114 | 25 | |||
4115 | 26 | #ifdef HAVE_UNISTD_H | ||
4116 | 27 | #include <unistd.h> | ||
4117 | 28 | #endif | ||
4118 | 29 | #ifdef HAVE_LOCALE_H | ||
4119 | 30 | #include <locale.h> | ||
4120 | 31 | #endif | ||
4121 | 32 | |||
4122 | 33 | #include <gdk/gdkkeysyms.h> | ||
4123 | 34 | #include <libxfce4ui/libxfce4ui.h> | ||
4124 | 35 | |||
4125 | 36 | #include <thunar/thunar-application.h> | ||
4126 | 37 | #include <thunar/thunar-browser.h> | ||
4127 | 38 | #include <thunar/thunar-clipboard-manager.h> | ||
4128 | 39 | #include <thunar/thunar-compact-view.h> | ||
4129 | 40 | #include <thunar/thunar-details-view.h> | ||
4130 | 41 | #include <thunar/thunar-dialogs.h> | ||
4131 | 42 | #include <thunar/thunar-shortcuts-pane.h> | ||
4132 | 43 | #include <thunar/thunar-gio-extensions.h> | ||
4133 | 44 | #include <thunar/thunar-gobject-extensions.h> | ||
4134 | 45 | #include <thunar/thunar-gtk-extensions.h> | ||
4135 | 46 | #include <thunar/thunar-icon-view.h> | ||
4136 | 47 | #include <thunar/thunar-launcher.h> | ||
4137 | 48 | #include <thunar/thunar-location-buttons.h> | ||
4138 | 49 | #include <thunar/thunar-location-dialog.h> | ||
4139 | 50 | #include <thunar/thunar-location-entry.h> | ||
4140 | 51 | #include <thunar/thunar-marshal.h> | ||
4141 | 52 | #include <thunar/thunar-pango-extensions.h> | ||
4142 | 53 | #include <thunar/thunar-preferences-dialog.h> | ||
4143 | 54 | #include <thunar/thunar-preferences.h> | ||
4144 | 55 | #include <thunar/thunar-private.h> | ||
4145 | 56 | #include <thunar/thunar-util.h> | ||
4146 | 57 | #include <thunar/thunar-statusbar.h> | ||
4147 | 58 | #include <thunar/thunar-stock.h> | ||
4148 | 59 | #include <thunar/thunar-trash-action.h> | ||
4149 | 60 | #include <thunar/thunar-tree-pane.h> | ||
4150 | 61 | #include <thunar/thunar-window.h> | ||
4151 | 62 | #include <thunar/thunar-window-ui.h> | ||
4152 | 63 | #include <thunar/thunar-device-monitor.h> | ||
4153 | 64 | |||
4154 | 65 | #include <glib.h> | ||
4155 | 66 | |||
4156 | 67 | |||
4157 | 68 | |||
4158 | 69 | /* Property identifiers */ | ||
4159 | 70 | enum | ||
4160 | 71 | { | ||
4161 | 72 | PROP_0, | ||
4162 | 73 | PROP_CURRENT_DIRECTORY, | ||
4163 | 74 | PROP_SHOW_HIDDEN, | ||
4164 | 75 | PROP_UI_MANAGER, | ||
4165 | 76 | PROP_ZOOM_LEVEL, | ||
4166 | 77 | }; | ||
4167 | 78 | |||
4168 | 79 | /* Signal identifiers */ | ||
4169 | 80 | enum | ||
4170 | 81 | { | ||
4171 | 82 | BACK, | ||
4172 | 83 | RELOAD, | ||
4173 | 84 | TOGGLE_SIDEPANE, | ||
4174 | 85 | TOGGLE_MENUBAR, | ||
4175 | 86 | ZOOM_IN, | ||
4176 | 87 | ZOOM_OUT, | ||
4177 | 88 | ZOOM_RESET, | ||
4178 | 89 | TAB_CHANGE, | ||
4179 | 90 | LAST_SIGNAL, | ||
4180 | 91 | }; | ||
4181 | 92 | |||
4182 | 93 | |||
4183 | 94 | |||
4184 | 95 | static void thunar_window_dispose (GObject *object); | ||
4185 | 96 | static void thunar_window_finalize (GObject *object); | ||
4186 | 97 | static void thunar_window_get_property (GObject *object, | ||
4187 | 98 | guint prop_id, | ||
4188 | 99 | GValue *value, | ||
4189 | 100 | GParamSpec *pspec); | ||
4190 | 101 | static void thunar_window_set_property (GObject *object, | ||
4191 | 102 | guint prop_id, | ||
4192 | 103 | const GValue *value, | ||
4193 | 104 | GParamSpec *pspec); | ||
4194 | 105 | static gboolean thunar_window_back (ThunarWindow *window); | ||
4195 | 106 | static gboolean thunar_window_reload (ThunarWindow *window); | ||
4196 | 107 | static gboolean thunar_window_toggle_sidepane (ThunarWindow *window); | ||
4197 | 108 | static gboolean thunar_window_toggle_menubar (ThunarWindow *window); | ||
4198 | 109 | static void thunar_window_toggle_menubar_deactivate (GtkWidget *menubar, | ||
4199 | 110 | ThunarWindow *window); | ||
4200 | 111 | static gboolean thunar_window_zoom_in (ThunarWindow *window); | ||
4201 | 112 | static gboolean thunar_window_zoom_out (ThunarWindow *window); | ||
4202 | 113 | static gboolean thunar_window_zoom_reset (ThunarWindow *window); | ||
4203 | 114 | static gboolean thunar_window_tab_change (ThunarWindow *window, | ||
4204 | 115 | gint nth); | ||
4205 | 116 | static void thunar_window_realize (GtkWidget *widget); | ||
4206 | 117 | static void thunar_window_unrealize (GtkWidget *widget); | ||
4207 | 118 | static gboolean thunar_window_configure_event (GtkWidget *widget, | ||
4208 | 119 | GdkEventConfigure *event); | ||
4209 | 120 | static void thunar_window_notebook_switch_page (GtkWidget *notebook, | ||
4210 | 121 | GtkWidget *page, | ||
4211 | 122 | guint page_num, | ||
4212 | 123 | ThunarWindow *window); | ||
4213 | 124 | static void thunar_window_notebook_page_added (GtkWidget *notebook, | ||
4214 | 125 | GtkWidget *page, | ||
4215 | 126 | guint page_num, | ||
4216 | 127 | ThunarWindow *window); | ||
4217 | 128 | static void thunar_window_notebook_page_removed (GtkWidget *notebook, | ||
4218 | 129 | GtkWidget *page, | ||
4219 | 130 | guint page_num, | ||
4220 | 131 | ThunarWindow *window); | ||
4221 | 132 | static gboolean thunar_window_notebook_button_press_event (GtkWidget *notebook, | ||
4222 | 133 | GdkEventButton *event, | ||
4223 | 134 | ThunarWindow *window); | ||
4224 | 135 | static gboolean thunar_window_notebook_popup_menu (GtkWidget *notebook, | ||
4225 | 136 | ThunarWindow *window); | ||
4226 | 137 | static gpointer thunar_window_notebook_create_window (GtkWidget *notebook, | ||
4227 | 138 | GtkWidget *page, | ||
4228 | 139 | gint x, | ||
4229 | 140 | gint y, | ||
4230 | 141 | ThunarWindow *window); | ||
4231 | 142 | static void thunar_window_notebook_insert (ThunarWindow *window, | ||
4232 | 143 | ThunarFile *directory); | ||
4233 | 144 | static void thunar_window_merge_custom_preferences (ThunarWindow *window); | ||
4234 | 145 | static gboolean thunar_window_bookmark_merge (gpointer user_data); | ||
4235 | 146 | static void thunar_window_merge_go_actions (ThunarWindow *window); | ||
4236 | 147 | static void thunar_window_install_location_bar (ThunarWindow *window, | ||
4237 | 148 | GType type); | ||
4238 | 149 | static void thunar_window_install_sidepane (ThunarWindow *window, | ||
4239 | 150 | GType type); | ||
4240 | 151 | static void thunar_window_start_open_location (ThunarWindow *window, | ||
4241 | 152 | const gchar *initial_text); | ||
4242 | 153 | static void thunar_window_action_open_new_tab (GtkAction *action, | ||
4243 | 154 | ThunarWindow *window); | ||
4244 | 155 | static void thunar_window_action_open_new_window (GtkAction *action, | ||
4245 | 156 | ThunarWindow *window); | ||
4246 | 157 | static void thunar_window_action_empty_trash (GtkAction *action, | ||
4247 | 158 | ThunarWindow *window); | ||
4248 | 159 | static void thunar_window_action_detach_tab (GtkAction *action, | ||
4249 | 160 | ThunarWindow *window); | ||
4250 | 161 | static void thunar_window_action_close_all_windows (GtkAction *action, | ||
4251 | 162 | ThunarWindow *window); | ||
4252 | 163 | static void thunar_window_action_close_tab (GtkAction *action, | ||
4253 | 164 | ThunarWindow *window); | ||
4254 | 165 | static void thunar_window_action_close_window (GtkAction *action, | ||
4255 | 166 | ThunarWindow *window); | ||
4256 | 167 | static void thunar_window_action_preferences (GtkAction *action, | ||
4257 | 168 | ThunarWindow *window); | ||
4258 | 169 | static void thunar_window_action_reload (GtkAction *action, | ||
4259 | 170 | ThunarWindow *window); | ||
4260 | 171 | static void thunar_window_action_pathbar_changed (GtkToggleAction *action, | ||
4261 | 172 | ThunarWindow *window); | ||
4262 | 173 | static void thunar_window_action_toolbar_changed (GtkToggleAction *action, | ||
4263 | 174 | ThunarWindow *window); | ||
4264 | 175 | static void thunar_window_action_shortcuts_changed (GtkToggleAction *action, | ||
4265 | 176 | ThunarWindow *window); | ||
4266 | 177 | static void thunar_window_action_tree_changed (GtkToggleAction *action, | ||
4267 | 178 | ThunarWindow *window); | ||
4268 | 179 | static void thunar_window_action_statusbar_changed (GtkToggleAction *action, | ||
4269 | 180 | ThunarWindow *window); | ||
4270 | 181 | static void thunar_window_action_menubar_changed (GtkToggleAction *action, | ||
4271 | 182 | ThunarWindow *window); | ||
4272 | 183 | static void thunar_window_action_zoom_in (GtkAction *action, | ||
4273 | 184 | ThunarWindow *window); | ||
4274 | 185 | static void thunar_window_action_zoom_out (GtkAction *action, | ||
4275 | 186 | ThunarWindow *window); | ||
4276 | 187 | static void thunar_window_action_zoom_reset (GtkAction *action, | ||
4277 | 188 | ThunarWindow *window); | ||
4278 | 189 | static void thunar_window_action_view_changed (GtkRadioAction *action, | ||
4279 | 190 | GtkRadioAction *current, | ||
4280 | 191 | ThunarWindow *window); | ||
4281 | 192 | static void thunar_window_action_go_up (GtkAction *action, | ||
4282 | 193 | ThunarWindow *window); | ||
4283 | 194 | static void thunar_window_action_open_home (GtkAction *action, | ||
4284 | 195 | ThunarWindow *window); | ||
4285 | 196 | static void thunar_window_action_open_desktop (GtkAction *action, | ||
4286 | 197 | ThunarWindow *window); | ||
4287 | 198 | static void thunar_window_action_open_templates (GtkAction *action, | ||
4288 | 199 | ThunarWindow *window); | ||
4289 | 200 | static void thunar_window_action_open_file_system (GtkAction *action, | ||
4290 | 201 | ThunarWindow *window); | ||
4291 | 202 | static void thunar_window_action_open_trash (GtkAction *action, | ||
4292 | 203 | ThunarWindow *window); | ||
4293 | 204 | static void thunar_window_action_open_network (GtkAction *action, | ||
4294 | 205 | ThunarWindow *window); | ||
4295 | 206 | static void thunar_window_action_open_bookmark (GtkAction *action, | ||
4296 | 207 | ThunarWindow *window); | ||
4297 | 208 | static void thunar_window_action_open_location (GtkAction *action, | ||
4298 | 209 | ThunarWindow *window); | ||
4299 | 210 | static void thunar_window_action_contents (GtkAction *action, | ||
4300 | 211 | ThunarWindow *window); | ||
4301 | 212 | static void thunar_window_action_about (GtkAction *action, | ||
4302 | 213 | ThunarWindow *window); | ||
4303 | 214 | static void thunar_window_action_show_hidden (GtkToggleAction *action, | ||
4304 | 215 | ThunarWindow *window); | ||
4305 | 216 | static void thunar_window_current_directory_changed (ThunarFile *current_directory, | ||
4306 | 217 | ThunarWindow *window); | ||
4307 | 218 | static void thunar_window_connect_proxy (GtkUIManager *manager, | ||
4308 | 219 | GtkAction *action, | ||
4309 | 220 | GtkWidget *proxy, | ||
4310 | 221 | ThunarWindow *window); | ||
4311 | 222 | static void thunar_window_disconnect_proxy (GtkUIManager *manager, | ||
4312 | 223 | GtkAction *action, | ||
4313 | 224 | GtkWidget *proxy, | ||
4314 | 225 | ThunarWindow *window); | ||
4315 | 226 | static void thunar_window_menu_item_selected (GtkWidget *menu_item, | ||
4316 | 227 | ThunarWindow *window); | ||
4317 | 228 | static void thunar_window_menu_item_deselected (GtkWidget *menu_item, | ||
4318 | 229 | ThunarWindow *window); | ||
4319 | 230 | static void thunar_window_update_custom_actions (ThunarView *view, | ||
4320 | 231 | GParamSpec *pspec, | ||
4321 | 232 | ThunarWindow *window); | ||
4322 | 233 | static void thunar_window_notify_loading (ThunarView *view, | ||
4323 | 234 | GParamSpec *pspec, | ||
4324 | 235 | ThunarWindow *window); | ||
4325 | 236 | static void thunar_window_device_pre_unmount (ThunarDeviceMonitor *device_monitor, | ||
4326 | 237 | ThunarDevice *device, | ||
4327 | 238 | GFile *root_file, | ||
4328 | 239 | ThunarWindow *window); | ||
4329 | 240 | static void thunar_window_device_changed (ThunarDeviceMonitor *device_monitor, | ||
4330 | 241 | ThunarDevice *device, | ||
4331 | 242 | ThunarWindow *window); | ||
4332 | 243 | static gboolean thunar_window_merge_idle (gpointer user_data); | ||
4333 | 244 | static void thunar_window_merge_idle_destroy (gpointer user_data); | ||
4334 | 245 | static gboolean thunar_window_save_paned (ThunarWindow *window); | ||
4335 | 246 | static gboolean thunar_window_save_geometry_timer (gpointer user_data); | ||
4336 | 247 | static void thunar_window_save_geometry_timer_destroy (gpointer user_data); | ||
4337 | 248 | static void thunar_window_set_zoom_level (ThunarWindow *window, | ||
4338 | 249 | ThunarZoomLevel zoom_level); | ||
4339 | 250 | |||
4340 | 251 | |||
4341 | 252 | |||
4342 | 253 | struct _ThunarWindowClass | ||
4343 | 254 | { | ||
4344 | 255 | GtkWindowClass __parent__; | ||
4345 | 256 | |||
4346 | 257 | /* internal action signals */ | ||
4347 | 258 | gboolean (*back) (ThunarWindow *window); | ||
4348 | 259 | gboolean (*reload) (ThunarWindow *window); | ||
4349 | 260 | gboolean (*toggle_sidepane) (ThunarWindow *window); | ||
4350 | 261 | gboolean (*toggle_menubar) (ThunarWindow *window); | ||
4351 | 262 | gboolean (*zoom_in) (ThunarWindow *window); | ||
4352 | 263 | gboolean (*zoom_out) (ThunarWindow *window); | ||
4353 | 264 | gboolean (*zoom_reset) (ThunarWindow *window); | ||
4354 | 265 | gboolean (*tab_change) (ThunarWindow *window, | ||
4355 | 266 | gint idx); | ||
4356 | 267 | }; | ||
4357 | 268 | |||
4358 | 269 | struct _ThunarWindow | ||
4359 | 270 | { | ||
4360 | 271 | GtkWindow __parent__; | ||
4361 | 272 | |||
4362 | 273 | /* support for custom preferences actions */ | ||
4363 | 274 | ThunarxProviderFactory *provider_factory; | ||
4364 | 275 | guint custom_preferences_merge_id; | ||
4365 | 276 | |||
4366 | 277 | /* UI manager merge ID for go menu actions */ | ||
4367 | 278 | guint go_items_actions_merge_id; | ||
4368 | 279 | |||
4369 | 280 | /* UI manager merge ID for the bookmark actions */ | ||
4370 | 281 | guint bookmark_items_actions_merge_id; | ||
4371 | 282 | GtkActionGroup *bookmark_action_group; | ||
4372 | 283 | GFile *bookmark_file; | ||
4373 | 284 | GFileMonitor *bookmark_monitor; | ||
4374 | 285 | guint bookmark_reload_idle_id; | ||
4375 | 286 | |||
4376 | 287 | ThunarClipboardManager *clipboard; | ||
4377 | 288 | |||
4378 | 289 | ThunarPreferences *preferences; | ||
4379 | 290 | |||
4380 | 291 | ThunarIconFactory *icon_factory; | ||
4381 | 292 | |||
4382 | 293 | GtkActionGroup *action_group; | ||
4383 | 294 | GtkUIManager *ui_manager; | ||
4384 | 295 | |||
4385 | 296 | /* to be able to change folder on "device-pre-unmount" if required */ | ||
4386 | 297 | ThunarDeviceMonitor *device_monitor; | ||
4387 | 298 | |||
4388 | 299 | /* closures for the menu_item_selected()/menu_item_deselected() callbacks */ | ||
4389 | 300 | GClosure *menu_item_selected_closure; | ||
4390 | 301 | GClosure *menu_item_deselected_closure; | ||
4391 | 302 | |||
4392 | 303 | /* custom menu actions for the file menu */ | ||
4393 | 304 | GtkActionGroup *custom_actions; | ||
4394 | 305 | guint custom_merge_id; | ||
4395 | 306 | |||
4396 | 307 | GtkWidget *table; | ||
4397 | 308 | GtkWidget *menubar; | ||
4398 | 309 | GtkWidget *spinner; | ||
4399 | 310 | GtkWidget *paned; | ||
4400 | 311 | GtkWidget *sidepane; | ||
4401 | 312 | GtkWidget *view_box; | ||
4402 | 313 | GtkWidget *notebook; | ||
4403 | 314 | GtkWidget *view; | ||
4404 | 315 | GtkWidget *statusbar; | ||
4405 | 316 | |||
4406 | 317 | GType view_type; | ||
4407 | 318 | GSList *view_bindings; | ||
4408 | 319 | |||
4409 | 320 | /* support for two different styles of location bars */ | ||
4410 | 321 | GtkWidget *location_bar; | ||
4411 | 322 | GtkWidget *location_toolbar; | ||
4412 | 323 | |||
4413 | 324 | ThunarLauncher *launcher; | ||
4414 | 325 | |||
4415 | 326 | ThunarFile *current_directory; | ||
4416 | 327 | |||
4417 | 328 | /* zoom-level support */ | ||
4418 | 329 | ThunarZoomLevel zoom_level; | ||
4419 | 330 | |||
4420 | 331 | /* menu merge idle source */ | ||
4421 | 332 | guint merge_idle_id; | ||
4422 | 333 | |||
4423 | 334 | /* support to remember window geometry */ | ||
4424 | 335 | guint save_geometry_timer_id; | ||
4425 | 336 | |||
4426 | 337 | /* support to toggle side pane using F9, | ||
4427 | 338 | * see the toggle_sidepane() function. | ||
4428 | 339 | */ | ||
4429 | 340 | GType toggle_sidepane_type; | ||
4430 | 341 | }; | ||
4431 | 342 | |||
4432 | 343 | |||
4433 | 344 | |||
4434 | 345 | static GtkActionEntry action_entries[] = | ||
4435 | 346 | { | ||
4436 | 347 | { "file-menu", NULL, N_ ("_File"), NULL, }, | ||
4437 | 348 | { "new-tab", "tab-new", N_ ("New _Tab"), "<control>T", N_ ("Open a new tab for the displayed location"), G_CALLBACK (thunar_window_action_open_new_tab), }, | ||
4438 | 349 | { "new-window", "window-new", N_ ("New _Window"), "<control>N", N_ ("Open a new Thunar window for the displayed location"), G_CALLBACK (thunar_window_action_open_new_window), }, | ||
4439 | 350 | { "sendto-menu", NULL, N_ ("_Send To"), NULL, }, | ||
4440 | 351 | { "empty-trash", NULL, N_ ("_Empty Trash"), NULL, N_ ("Delete all files and folders in the Trash"), G_CALLBACK (thunar_window_action_empty_trash), }, | ||
4441 | 352 | { "detach-tab", NULL, N_ ("Detac_h Tab"), NULL, N_ ("Open current folder in a new window"), G_CALLBACK (thunar_window_action_detach_tab), }, | ||
4442 | 353 | { "close-all-windows", NULL, N_ ("Close _All Windows"), "<control><shift>W", N_ ("Close all Thunar windows"), G_CALLBACK (thunar_window_action_close_all_windows), }, | ||
4443 | 354 | { "close-tab", GTK_STOCK_CLOSE, N_ ("C_lose Tab"), "<control>W", N_ ("Close this folder"), G_CALLBACK (thunar_window_action_close_tab), }, | ||
4444 | 355 | { "close-window", GTK_STOCK_QUIT, N_ ("_Close Window"), "<control>Q", N_ ("Close this window"), G_CALLBACK (thunar_window_action_close_window), }, | ||
4445 | 356 | { "edit-menu", NULL, N_ ("_Edit"), NULL, }, | ||
4446 | 357 | { "preferences", GTK_STOCK_PREFERENCES, N_ ("Pr_eferences..."), NULL, N_ ("Edit Thunars Preferences"), G_CALLBACK (thunar_window_action_preferences), }, | ||
4447 | 358 | { "view-menu", NULL, N_ ("_View"), NULL, }, | ||
4448 | 359 | { "reload", GTK_STOCK_REFRESH, N_ ("_Reload"), "<control>R", N_ ("Reload the current folder"), G_CALLBACK (thunar_window_action_reload), }, | ||
4449 | 360 | { "view-location-selector-menu", NULL, N_ ("_Location Selector"), NULL, }, | ||
4450 | 361 | { "view-side-pane-menu", NULL, N_ ("_Side Pane"), NULL, }, | ||
4451 | 362 | { "zoom-in", GTK_STOCK_ZOOM_IN, N_ ("Zoom I_n"), "<control>plus", N_ ("Show the contents in more detail"), G_CALLBACK (thunar_window_action_zoom_in), }, | ||
4452 | 363 | { "zoom-out", GTK_STOCK_ZOOM_OUT, N_ ("Zoom _Out"), "<control>minus", N_ ("Show the contents in less detail"), G_CALLBACK (thunar_window_action_zoom_out), }, | ||
4453 | 364 | { "zoom-reset", GTK_STOCK_ZOOM_100, N_ ("Normal Si_ze"), "<control>0", N_ ("Show the contents at the normal size"), G_CALLBACK (thunar_window_action_zoom_reset), }, | ||
4454 | 365 | { "go-menu", NULL, N_ ("_Go"), NULL, }, | ||
4455 | 366 | { "open-parent", GTK_STOCK_GO_UP, N_ ("Open _Parent"), "<alt>Up", N_ ("Open the parent folder"), G_CALLBACK (thunar_window_action_go_up), }, | ||
4456 | 367 | { "open-home", GTK_STOCK_HOME, N_ ("_Home"), "<alt>Home", N_ ("Go to the home folder"), G_CALLBACK (thunar_window_action_open_home), }, | ||
4457 | 368 | { "open-desktop", THUNAR_STOCK_DESKTOP, N_ ("Desktop"), NULL, N_ ("Go to the desktop folder"), G_CALLBACK (thunar_window_action_open_desktop), }, | ||
4458 | 369 | { "open-file-system", GTK_STOCK_HARDDISK, N_ ("File System"), NULL, N_ ("Browse the file system"), G_CALLBACK (thunar_window_action_open_file_system), }, | ||
4459 | 370 | { "open-network", GTK_STOCK_NETWORK, N_("B_rowse Network"), NULL, N_ ("Browse local network connections"), G_CALLBACK (thunar_window_action_open_network), }, | ||
4460 | 371 | { "open-templates", THUNAR_STOCK_TEMPLATES, N_("T_emplates"), NULL, N_ ("Go to the templates folder"), G_CALLBACK (thunar_window_action_open_templates), }, | ||
4461 | 372 | { "open-location", NULL, N_ ("_Open Location..."), "<control>L", N_ ("Specify a location to open"), G_CALLBACK (thunar_window_action_open_location), }, | ||
4462 | 373 | { "help-menu", NULL, N_ ("_Help"), NULL, }, | ||
4463 | 374 | { "contents", GTK_STOCK_HELP, N_ ("_Contents"), "F1", N_ ("Display Thunar user manual"), G_CALLBACK (thunar_window_action_contents), }, | ||
4464 | 375 | { "about", GTK_STOCK_ABOUT, N_ ("_About"), NULL, N_ ("Display information about Thunar"), G_CALLBACK (thunar_window_action_about), }, | ||
4465 | 376 | }; | ||
4466 | 377 | |||
4467 | 378 | static const GtkToggleActionEntry toggle_action_entries[] = | ||
4468 | 379 | { | ||
4469 | 380 | { "show-hidden", NULL, N_ ("Show _Hidden Files"), "<control>H", N_ ("Toggles the display of hidden files in the current window"), G_CALLBACK (thunar_window_action_show_hidden), FALSE, }, | ||
4470 | 381 | { "view-location-selector-pathbar", NULL, N_ ("_Pathbar Style"), NULL, N_ ("Modern approach with buttons that correspond to folders"), G_CALLBACK (thunar_window_action_pathbar_changed), FALSE, }, | ||
4471 | 382 | { "view-location-selector-toolbar", NULL, N_ ("_Toolbar Style"), NULL, N_ ("Traditional approach with location bar and navigation buttons"), G_CALLBACK (thunar_window_action_toolbar_changed), FALSE, }, | ||
4472 | 383 | { "view-side-pane-shortcuts", NULL, N_ ("_Shortcuts"), "<control>B", N_ ("Toggles the visibility of the shortcuts pane"), G_CALLBACK (thunar_window_action_shortcuts_changed), FALSE, }, | ||
4473 | 384 | { "view-side-pane-tree", NULL, N_ ("_Tree"), "<control>E", N_ ("Toggles the visibility of the tree pane"), G_CALLBACK (thunar_window_action_tree_changed), FALSE, }, | ||
4474 | 385 | { "view-statusbar", NULL, N_ ("St_atusbar"), NULL, N_ ("Change the visibility of this window's statusbar"), G_CALLBACK (thunar_window_action_statusbar_changed), FALSE, }, | ||
4475 | 386 | { "view-menubar", NULL, N_ ("_Menubar"), "<control>M", N_ ("Change the visibility of this window's menubar"), G_CALLBACK (thunar_window_action_menubar_changed), TRUE, }, | ||
4476 | 387 | }; | ||
4477 | 388 | |||
4478 | 389 | |||
4479 | 390 | |||
4480 | 391 | static guint window_signals[LAST_SIGNAL]; | ||
4481 | 392 | |||
4482 | 393 | |||
4483 | 394 | |||
4484 | 395 | G_DEFINE_TYPE_WITH_CODE (ThunarWindow, thunar_window, GTK_TYPE_WINDOW, | ||
4485 | 396 | G_IMPLEMENT_INTERFACE (THUNAR_TYPE_BROWSER, NULL)) | ||
4486 | 397 | |||
4487 | 398 | |||
4488 | 399 | |||
4489 | 400 | static void | ||
4490 | 401 | thunar_window_class_init (ThunarWindowClass *klass) | ||
4491 | 402 | { | ||
4492 | 403 | GtkWidgetClass *gtkwidget_class; | ||
4493 | 404 | GtkBindingSet *binding_set; | ||
4494 | 405 | GObjectClass *gobject_class; | ||
4495 | 406 | guint i; | ||
4496 | 407 | |||
4497 | 408 | gobject_class = G_OBJECT_CLASS (klass); | ||
4498 | 409 | gobject_class->dispose = thunar_window_dispose; | ||
4499 | 410 | gobject_class->finalize = thunar_window_finalize; | ||
4500 | 411 | gobject_class->get_property = thunar_window_get_property; | ||
4501 | 412 | gobject_class->set_property = thunar_window_set_property; | ||
4502 | 413 | |||
4503 | 414 | gtkwidget_class = GTK_WIDGET_CLASS (klass); | ||
4504 | 415 | gtkwidget_class->realize = thunar_window_realize; | ||
4505 | 416 | gtkwidget_class->unrealize = thunar_window_unrealize; | ||
4506 | 417 | gtkwidget_class->configure_event = thunar_window_configure_event; | ||
4507 | 418 | |||
4508 | 419 | klass->back = thunar_window_back; | ||
4509 | 420 | klass->reload = thunar_window_reload; | ||
4510 | 421 | klass->toggle_sidepane = thunar_window_toggle_sidepane; | ||
4511 | 422 | klass->toggle_menubar = thunar_window_toggle_menubar; | ||
4512 | 423 | klass->zoom_in = thunar_window_zoom_in; | ||
4513 | 424 | klass->zoom_out = thunar_window_zoom_out; | ||
4514 | 425 | klass->zoom_reset = thunar_window_zoom_reset; | ||
4515 | 426 | klass->tab_change = thunar_window_tab_change; | ||
4516 | 427 | |||
4517 | 428 | /** | ||
4518 | 429 | * ThunarWindow:current-directory: | ||
4519 | 430 | * | ||
4520 | 431 | * The directory currently displayed within this #ThunarWindow | ||
4521 | 432 | * or %NULL. | ||
4522 | 433 | **/ | ||
4523 | 434 | g_object_class_install_property (gobject_class, | ||
4524 | 435 | PROP_CURRENT_DIRECTORY, | ||
4525 | 436 | g_param_spec_object ("current-directory", | ||
4526 | 437 | "current-directory", | ||
4527 | 438 | "current-directory", | ||
4528 | 439 | THUNAR_TYPE_FILE, | ||
4529 | 440 | EXO_PARAM_READWRITE)); | ||
4530 | 441 | |||
4531 | 442 | /** | ||
4532 | 443 | * ThunarWindow:show-hidden: | ||
4533 | 444 | * | ||
4534 | 445 | * Whether to show hidden files in the current window. | ||
4535 | 446 | **/ | ||
4536 | 447 | g_object_class_install_property (gobject_class, | ||
4537 | 448 | PROP_SHOW_HIDDEN, | ||
4538 | 449 | g_param_spec_boolean ("show-hidden", | ||
4539 | 450 | "show-hidden", | ||
4540 | 451 | "show-hidden", | ||
4541 | 452 | FALSE, | ||
4542 | 453 | EXO_PARAM_READABLE)); | ||
4543 | 454 | |||
4544 | 455 | /** | ||
4545 | 456 | * ThunarWindow:ui-manager: | ||
4546 | 457 | * | ||
4547 | 458 | * The #GtkUIManager used for this #ThunarWindow. This property | ||
4548 | 459 | * can only be read and is garantied to always contain a valid | ||
4549 | 460 | * #GtkUIManager instance (thus it's never %NULL). | ||
4550 | 461 | **/ | ||
4551 | 462 | g_object_class_install_property (gobject_class, | ||
4552 | 463 | PROP_UI_MANAGER, | ||
4553 | 464 | g_param_spec_object ("ui-manager", | ||
4554 | 465 | "ui-manager", | ||
4555 | 466 | "ui-manager", | ||
4556 | 467 | GTK_TYPE_UI_MANAGER, | ||
4557 | 468 | EXO_PARAM_READABLE)); | ||
4558 | 469 | |||
4559 | 470 | /** | ||
4560 | 471 | * ThunarWindow:zoom-level: | ||
4561 | 472 | * | ||
4562 | 473 | * The #ThunarZoomLevel applied to the #ThunarView currently | ||
4563 | 474 | * shown within this window. | ||
4564 | 475 | **/ | ||
4565 | 476 | g_object_class_install_property (gobject_class, | ||
4566 | 477 | PROP_ZOOM_LEVEL, | ||
4567 | 478 | g_param_spec_enum ("zoom-level", | ||
4568 | 479 | "zoom-level", | ||
4569 | 480 | "zoom-level", | ||
4570 | 481 | THUNAR_TYPE_ZOOM_LEVEL, | ||
4571 | 482 | THUNAR_ZOOM_LEVEL_NORMAL, | ||
4572 | 483 | EXO_PARAM_READWRITE)); | ||
4573 | 484 | |||
4574 | 485 | /** | ||
4575 | 486 | * ThunarWindow::back: | ||
4576 | 487 | * @window : a #ThunarWindow instance. | ||
4577 | 488 | * | ||
4578 | 489 | * Emitted whenever the user requests to go to the | ||
4579 | 490 | * previous visited folder. This is an internal | ||
4580 | 491 | * signal used to bind the action to keys. | ||
4581 | 492 | **/ | ||
4582 | 493 | window_signals[BACK] = | ||
4583 | 494 | g_signal_new (I_("back"), | ||
4584 | 495 | G_TYPE_FROM_CLASS (klass), | ||
4585 | 496 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4586 | 497 | G_STRUCT_OFFSET (ThunarWindowClass, back), | ||
4587 | 498 | g_signal_accumulator_true_handled, NULL, | ||
4588 | 499 | _thunar_marshal_BOOLEAN__VOID, | ||
4589 | 500 | G_TYPE_BOOLEAN, 0); | ||
4590 | 501 | |||
4591 | 502 | /** | ||
4592 | 503 | * ThunarWindow::reload: | ||
4593 | 504 | * @window : a #ThunarWindow instance. | ||
4594 | 505 | * | ||
4595 | 506 | * Emitted whenever the user requests to reload the contents | ||
4596 | 507 | * of the currently displayed folder. This is an internal | ||
4597 | 508 | * signal used to bind the action to keys. | ||
4598 | 509 | **/ | ||
4599 | 510 | window_signals[RELOAD] = | ||
4600 | 511 | g_signal_new (I_("reload"), | ||
4601 | 512 | G_TYPE_FROM_CLASS (klass), | ||
4602 | 513 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4603 | 514 | G_STRUCT_OFFSET (ThunarWindowClass, reload), | ||
4604 | 515 | g_signal_accumulator_true_handled, NULL, | ||
4605 | 516 | _thunar_marshal_BOOLEAN__VOID, | ||
4606 | 517 | G_TYPE_BOOLEAN, 0); | ||
4607 | 518 | |||
4608 | 519 | /** | ||
4609 | 520 | * ThunarWindow::toggle-sidepane: | ||
4610 | 521 | * @window : a #ThunarWindow instance. | ||
4611 | 522 | * | ||
4612 | 523 | * Emitted whenever the user toggles the visibility of the | ||
4613 | 524 | * sidepane. This is an internal signal used to bind the | ||
4614 | 525 | * action to keys. | ||
4615 | 526 | **/ | ||
4616 | 527 | window_signals[TOGGLE_SIDEPANE] = | ||
4617 | 528 | g_signal_new (I_("toggle-sidepane"), | ||
4618 | 529 | G_TYPE_FROM_CLASS (klass), | ||
4619 | 530 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4620 | 531 | G_STRUCT_OFFSET (ThunarWindowClass, toggle_sidepane), | ||
4621 | 532 | g_signal_accumulator_true_handled, NULL, | ||
4622 | 533 | _thunar_marshal_BOOLEAN__VOID, | ||
4623 | 534 | G_TYPE_BOOLEAN, 0); | ||
4624 | 535 | |||
4625 | 536 | /** | ||
4626 | 537 | * ThunarWindow::toggle-sidepane: | ||
4627 | 538 | * @window : a #ThunarWindow instance. | ||
4628 | 539 | * | ||
4629 | 540 | * Emitted whenever the user toggles the visibility of the | ||
4630 | 541 | * sidepane. This is an internal signal used to bind the | ||
4631 | 542 | * action to keys. | ||
4632 | 543 | **/ | ||
4633 | 544 | window_signals[TOGGLE_MENUBAR] = | ||
4634 | 545 | g_signal_new (I_("toggle-menubar"), | ||
4635 | 546 | G_TYPE_FROM_CLASS (klass), | ||
4636 | 547 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4637 | 548 | G_STRUCT_OFFSET (ThunarWindowClass, toggle_menubar), | ||
4638 | 549 | g_signal_accumulator_true_handled, NULL, | ||
4639 | 550 | _thunar_marshal_BOOLEAN__VOID, | ||
4640 | 551 | G_TYPE_BOOLEAN, 0); | ||
4641 | 552 | |||
4642 | 553 | /** | ||
4643 | 554 | * ThunarWindow::zoom-in: | ||
4644 | 555 | * @window : a #ThunarWindow instance. | ||
4645 | 556 | * | ||
4646 | 557 | * Emitted whenever the user requests to zoom in. This | ||
4647 | 558 | * is an internal signal used to bind the action to keys. | ||
4648 | 559 | **/ | ||
4649 | 560 | window_signals[ZOOM_IN] = | ||
4650 | 561 | g_signal_new (I_("zoom-in"), | ||
4651 | 562 | G_TYPE_FROM_CLASS (klass), | ||
4652 | 563 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4653 | 564 | G_STRUCT_OFFSET (ThunarWindowClass, zoom_in), | ||
4654 | 565 | g_signal_accumulator_true_handled, NULL, | ||
4655 | 566 | _thunar_marshal_BOOLEAN__VOID, | ||
4656 | 567 | G_TYPE_BOOLEAN, 0); | ||
4657 | 568 | |||
4658 | 569 | /** | ||
4659 | 570 | * ThunarWindow::zoom-out: | ||
4660 | 571 | * @window : a #ThunarWindow instance. | ||
4661 | 572 | * | ||
4662 | 573 | * Emitted whenever the user requests to zoom out. This | ||
4663 | 574 | * is an internal signal used to bind the action to keys. | ||
4664 | 575 | **/ | ||
4665 | 576 | window_signals[ZOOM_OUT] = | ||
4666 | 577 | g_signal_new (I_("zoom-out"), | ||
4667 | 578 | G_TYPE_FROM_CLASS (klass), | ||
4668 | 579 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4669 | 580 | G_STRUCT_OFFSET (ThunarWindowClass, zoom_out), | ||
4670 | 581 | g_signal_accumulator_true_handled, NULL, | ||
4671 | 582 | _thunar_marshal_BOOLEAN__VOID, | ||
4672 | 583 | G_TYPE_BOOLEAN, 0); | ||
4673 | 584 | |||
4674 | 585 | /** | ||
4675 | 586 | * ThunarWindow::zoom-reset: | ||
4676 | 587 | * @window : a #ThunarWindow instance. | ||
4677 | 588 | * | ||
4678 | 589 | * Emitted whenever the user requests reset the zoom level. | ||
4679 | 590 | * This is an internal signal used to bind the action to keys. | ||
4680 | 591 | **/ | ||
4681 | 592 | window_signals[ZOOM_RESET] = | ||
4682 | 593 | g_signal_new (I_("zoom-reset"), | ||
4683 | 594 | G_TYPE_FROM_CLASS (klass), | ||
4684 | 595 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4685 | 596 | G_STRUCT_OFFSET (ThunarWindowClass, zoom_reset), | ||
4686 | 597 | g_signal_accumulator_true_handled, NULL, | ||
4687 | 598 | _thunar_marshal_BOOLEAN__VOID, | ||
4688 | 599 | G_TYPE_BOOLEAN, 0); | ||
4689 | 600 | |||
4690 | 601 | /** | ||
4691 | 602 | * ThunarWindow::tab-chage: | ||
4692 | 603 | * @window : a #ThunarWindow instance. | ||
4693 | 604 | * @idx : tab index, | ||
4694 | 605 | * | ||
4695 | 606 | * Emitted whenever the user uses a Alt+N combination to | ||
4696 | 607 | * switch tabs. | ||
4697 | 608 | **/ | ||
4698 | 609 | window_signals[TAB_CHANGE] = | ||
4699 | 610 | g_signal_new (I_("tab-change"), | ||
4700 | 611 | G_TYPE_FROM_CLASS (klass), | ||
4701 | 612 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | ||
4702 | 613 | G_STRUCT_OFFSET (ThunarWindowClass, tab_change), | ||
4703 | 614 | g_signal_accumulator_true_handled, NULL, | ||
4704 | 615 | _thunar_marshal_BOOLEAN__INT, | ||
4705 | 616 | G_TYPE_BOOLEAN, 1, | ||
4706 | 617 | G_TYPE_INT); | ||
4707 | 618 | |||
4708 | 619 | /* setup the key bindings for the windows */ | ||
4709 | 620 | binding_set = gtk_binding_set_by_class (klass); | ||
4710 | 621 | gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, "back", 0); | ||
4711 | 622 | gtk_binding_entry_add_signal (binding_set, GDK_F5, 0, "reload", 0); | ||
4712 | 623 | gtk_binding_entry_add_signal (binding_set, GDK_F9, 0, "toggle-sidepane", 0); | ||
4713 | 624 | gtk_binding_entry_add_signal (binding_set, GDK_F10, 0, "toggle-menubar", 0); | ||
4714 | 625 | gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, GDK_CONTROL_MASK, "zoom-in", 0); | ||
4715 | 626 | gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, GDK_CONTROL_MASK, "zoom-out", 0); | ||
4716 | 627 | gtk_binding_entry_add_signal (binding_set, GDK_KP_0, GDK_CONTROL_MASK, "zoom-reset", 0); | ||
4717 | 628 | gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, GDK_CONTROL_MASK, "zoom-reset", 0); | ||
4718 | 629 | |||
4719 | 630 | /* setup the key bindings for Alt+N */ | ||
4720 | 631 | for (i = 0; i < 10; i++) | ||
4721 | 632 | { | ||
4722 | 633 | gtk_binding_entry_add_signal (binding_set, GDK_0 + i, GDK_MOD1_MASK, | ||
4723 | 634 | "tab-change", 1, G_TYPE_UINT, i - 1); | ||
4724 | 635 | } | ||
4725 | 636 | } | ||
4726 | 637 | |||
4727 | 638 | |||
4728 | 639 | |||
4729 | 640 | static inline gint | ||
4730 | 641 | view_type2index (GType type) | ||
4731 | 642 | { | ||
4732 | 643 | /* this necessary for platforms where sizeof(GType) != sizeof(gint), | ||
4733 | 644 | * see http://bugzilla.xfce.org/show_bug.cgi?id=2726 for details. | ||
4734 | 645 | */ | ||
4735 | 646 | if (sizeof (GType) == sizeof (gint)) | ||
4736 | 647 | { | ||
4737 | 648 | /* no need to map anything */ | ||
4738 | 649 | return (gint) type; | ||
4739 | 650 | } | ||
4740 | 651 | else | ||
4741 | 652 | { | ||
4742 | 653 | /* map from types to unique indices */ | ||
4743 | 654 | if (G_LIKELY (type == THUNAR_TYPE_COMPACT_VIEW)) | ||
4744 | 655 | return 0; | ||
4745 | 656 | else if (type == THUNAR_TYPE_DETAILS_VIEW) | ||
4746 | 657 | return 1; | ||
4747 | 658 | else | ||
4748 | 659 | return 2; | ||
4749 | 660 | } | ||
4750 | 661 | } | ||
4751 | 662 | |||
4752 | 663 | |||
4753 | 664 | |||
4754 | 665 | static inline GType | ||
4755 | 666 | view_index2type (gint idx) | ||
4756 | 667 | { | ||
4757 | 668 | /* this necessary for platforms where sizeof(GType) != sizeof(gint), | ||
4758 | 669 | * see http://bugzilla.xfce.org/show_bug.cgi?id=2726 for details. | ||
4759 | 670 | */ | ||
4760 | 671 | if (sizeof (GType) == sizeof (gint)) | ||
4761 | 672 | { | ||
4762 | 673 | /* no need to map anything */ | ||
4763 | 674 | return (GType) idx; | ||
4764 | 675 | } | ||
4765 | 676 | else | ||
4766 | 677 | { | ||
4767 | 678 | /* map from indices to unique types */ | ||
4768 | 679 | switch (idx) | ||
4769 | 680 | { | ||
4770 | 681 | case 0: return THUNAR_TYPE_COMPACT_VIEW; | ||
4771 | 682 | case 1: return THUNAR_TYPE_DETAILS_VIEW; | ||
4772 | 683 | default: return THUNAR_TYPE_ICON_VIEW; | ||
4773 | 684 | } | ||
4774 | 685 | } | ||
4775 | 686 | } | ||
4776 | 687 | |||
4777 | 688 | |||
4778 | 689 | |||
4779 | 690 | static void | ||
4780 | 691 | thunar_window_init (ThunarWindow *window) | ||
4781 | 692 | { | ||
4782 | 693 | GtkRadioAction *radio_action; | ||
4783 | 694 | GtkAccelGroup *accel_group; | ||
4784 | 695 | GtkWidget *label; | ||
4785 | 696 | GtkWidget *infobar; | ||
4786 | 697 | GtkWidget *item; | ||
4787 | 698 | GtkAction *action; | ||
4788 | 699 | gboolean last_show_hidden; | ||
4789 | 700 | gboolean last_menubar_visible; | ||
4790 | 701 | GSList *group; | ||
4791 | 702 | gchar *last_location_bar; | ||
4792 | 703 | gchar *last_side_pane; | ||
4793 | 704 | GType type; | ||
4794 | 705 | gint last_separator_position; | ||
4795 | 706 | gint last_window_width; | ||
4796 | 707 | gint last_window_height; | ||
4797 | 708 | gboolean last_window_maximized; | ||
4798 | 709 | gboolean last_statusbar_visible; | ||
4799 | 710 | GtkRcStyle *style; | ||
4800 | 711 | |||
4801 | 712 | /* unset the view type */ | ||
4802 | 713 | window->view_type = G_TYPE_NONE; | ||
4803 | 714 | |||
4804 | 715 | /* grab a reference on the provider factory */ | ||
4805 | 716 | window->provider_factory = thunarx_provider_factory_get_default (); | ||
4806 | 717 | |||
4807 | 718 | /* grab a reference on the preferences */ | ||
4808 | 719 | window->preferences = thunar_preferences_get (); | ||
4809 | 720 | |||
4810 | 721 | /* get all properties for init */ | ||
4811 | 722 | g_object_get (G_OBJECT (window->preferences), | ||
4812 | 723 | "last-show-hidden", &last_show_hidden, | ||
4813 | 724 | "last-window-width", &last_window_width, | ||
4814 | 725 | "last-window-height", &last_window_height, | ||
4815 | 726 | "last-window-maximized", &last_window_maximized, | ||
4816 | 727 | "last-menubar-visible", &last_menubar_visible, | ||
4817 | 728 | "last-separator-position", &last_separator_position, | ||
4818 | 729 | "last-location-bar", &last_location_bar, | ||
4819 | 730 | "last-side-pane", &last_side_pane, | ||
4820 | 731 | "last-statusbar-visible", &last_statusbar_visible, | ||
4821 | 732 | NULL); | ||
4822 | 733 | |||
4823 | 734 | /* connect to the volume monitor */ | ||
4824 | 735 | window->device_monitor = thunar_device_monitor_get (); | ||
4825 | 736 | g_signal_connect (window->device_monitor, "device-pre-unmount", G_CALLBACK (thunar_window_device_pre_unmount), window); | ||
4826 | 737 | g_signal_connect (window->device_monitor, "device-removed", G_CALLBACK (thunar_window_device_changed), window); | ||
4827 | 738 | g_signal_connect (window->device_monitor, "device-changed", G_CALLBACK (thunar_window_device_changed), window); | ||
4828 | 739 | |||
4829 | 740 | /* allocate a closure for the menu_item_selected() callback */ | ||
4830 | 741 | window->menu_item_selected_closure = g_cclosure_new_object (G_CALLBACK (thunar_window_menu_item_selected), G_OBJECT (window)); | ||
4831 | 742 | g_closure_ref (window->menu_item_selected_closure); | ||
4832 | 743 | g_closure_sink (window->menu_item_selected_closure); | ||
4833 | 744 | |||
4834 | 745 | /* allocate a closure for the menu_item_deselected() callback */ | ||
4835 | 746 | window->menu_item_deselected_closure = g_cclosure_new_object (G_CALLBACK (thunar_window_menu_item_deselected), G_OBJECT (window)); | ||
4836 | 747 | g_closure_ref (window->menu_item_deselected_closure); | ||
4837 | 748 | g_closure_sink (window->menu_item_deselected_closure); | ||
4838 | 749 | window->icon_factory = thunar_icon_factory_get_default (); | ||
4839 | 750 | |||
4840 | 751 | /* setup the action group for this window */ | ||
4841 | 752 | window->action_group = gtk_action_group_new ("ThunarWindow"); | ||
4842 | 753 | gtk_action_group_set_translation_domain (window->action_group, GETTEXT_PACKAGE); | ||
4843 | 754 | gtk_action_group_add_actions (window->action_group, action_entries, G_N_ELEMENTS (action_entries), GTK_WIDGET (window)); | ||
4844 | 755 | gtk_action_group_add_toggle_actions (window->action_group, toggle_action_entries, G_N_ELEMENTS (toggle_action_entries), GTK_WIDGET (window)); | ||
4845 | 756 | |||
4846 | 757 | /* initialize the "show-hidden" action using the last value from the preferences */ | ||
4847 | 758 | action = gtk_action_group_get_action (window->action_group, "show-hidden"); | ||
4848 | 759 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), last_show_hidden); | ||
4849 | 760 | |||
4850 | 761 | /* | ||
4851 | 762 | * add view options | ||
4852 | 763 | */ | ||
4853 | 764 | radio_action = gtk_radio_action_new ("view-as-icons", _("View as _Icons"), _("Display folder content in an icon view"), | ||
4854 | 765 | NULL, view_type2index (THUNAR_TYPE_ICON_VIEW)); | ||
4855 | 766 | gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "<control>1"); | ||
4856 | 767 | gtk_radio_action_set_group (radio_action, NULL); | ||
4857 | 768 | group = gtk_radio_action_get_group (radio_action); | ||
4858 | 769 | g_object_unref (G_OBJECT (radio_action)); | ||
4859 | 770 | |||
4860 | 771 | radio_action = gtk_radio_action_new ("view-as-detailed-list", _("View as _Detailed List"), _("Display folder content in a detailed list view"), | ||
4861 | 772 | NULL, view_type2index (THUNAR_TYPE_DETAILS_VIEW)); | ||
4862 | 773 | gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "<control>2"); | ||
4863 | 774 | gtk_radio_action_set_group (radio_action, group); | ||
4864 | 775 | group = gtk_radio_action_get_group (radio_action); | ||
4865 | 776 | g_object_unref (G_OBJECT (radio_action)); | ||
4866 | 777 | |||
4867 | 778 | radio_action = gtk_radio_action_new ("view-as-compact-list", _("View as _Compact List"), _("Display folder content in a compact list view"), | ||
4868 | 779 | NULL, view_type2index (THUNAR_TYPE_COMPACT_VIEW)); | ||
4869 | 780 | gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "<control>3"); | ||
4870 | 781 | gtk_radio_action_set_group (radio_action, group); | ||
4871 | 782 | group = gtk_radio_action_get_group (radio_action); | ||
4872 | 783 | g_object_unref (G_OBJECT (radio_action)); | ||
4873 | 784 | |||
4874 | 785 | window->ui_manager = gtk_ui_manager_new (); | ||
4875 | 786 | g_signal_connect (G_OBJECT (window->ui_manager), "connect-proxy", G_CALLBACK (thunar_window_connect_proxy), window); | ||
4876 | 787 | g_signal_connect (G_OBJECT (window->ui_manager), "disconnect-proxy", G_CALLBACK (thunar_window_disconnect_proxy), window); | ||
4877 | 788 | gtk_ui_manager_insert_action_group (window->ui_manager, window->action_group, 0); | ||
4878 | 789 | gtk_ui_manager_add_ui_from_string (window->ui_manager, thunar_window_ui, thunar_window_ui_length, NULL); | ||
4879 | 790 | |||
4880 | 791 | accel_group = gtk_ui_manager_get_accel_group (window->ui_manager); | ||
4881 | 792 | gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); | ||
4882 | 793 | |||
4883 | 794 | /* setup the launcher support */ | ||
4884 | 795 | window->launcher = thunar_launcher_new (); | ||
4885 | 796 | thunar_launcher_set_widget (window->launcher, GTK_WIDGET (window)); | ||
4886 | 797 | thunar_component_set_ui_manager (THUNAR_COMPONENT (window->launcher), window->ui_manager); | ||
4887 | 798 | exo_binding_new (G_OBJECT (window), "current-directory", G_OBJECT (window->launcher), "current-directory"); | ||
4888 | 799 | g_signal_connect_swapped (G_OBJECT (window->launcher), "change-directory", G_CALLBACK (thunar_window_set_current_directory), window); | ||
4889 | 800 | g_signal_connect_swapped (G_OBJECT (window->launcher), "open-new-tab", G_CALLBACK (thunar_window_notebook_insert), window); | ||
4890 | 801 | |||
4891 | 802 | /* determine the default window size from the preferences */ | ||
4892 | 803 | gtk_window_set_default_size (GTK_WINDOW (window), last_window_width, last_window_height); | ||
4893 | 804 | |||
4894 | 805 | /* restore the maxized state of the window */ | ||
4895 | 806 | if (G_UNLIKELY (last_window_maximized)) | ||
4896 | 807 | gtk_window_maximize (GTK_WINDOW (window)); | ||
4897 | 808 | |||
4898 | 809 | window->table = gtk_table_new (6, 1, FALSE); | ||
4899 | 810 | gtk_container_add (GTK_CONTAINER (window), window->table); | ||
4900 | 811 | gtk_widget_show (window->table); | ||
4901 | 812 | |||
4902 | 813 | window->menubar = gtk_ui_manager_get_widget (window->ui_manager, "/main-menu"); | ||
4903 | 814 | gtk_table_attach (GTK_TABLE (window->table), window->menubar, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); | ||
4904 | 815 | |||
4905 | 816 | /* update menubar visibiliy */ | ||
4906 | 817 | action = gtk_action_group_get_action (window->action_group, "view-menubar"); | ||
4907 | 818 | g_signal_connect (G_OBJECT (window->menubar), "deactivate", G_CALLBACK (thunar_window_toggle_menubar_deactivate), window); | ||
4908 | 819 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), last_menubar_visible); | ||
4909 | 820 | |||
4910 | 821 | /* append the menu item for the spinner */ | ||
4911 | 822 | item = gtk_menu_item_new (); | ||
4912 | 823 | gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE); | ||
4913 | 824 | gtk_menu_item_set_right_justified (GTK_MENU_ITEM (item), TRUE); | ||
4914 | 825 | gtk_menu_shell_append (GTK_MENU_SHELL (window->menubar), item); | ||
4915 | 826 | gtk_widget_show (item); | ||
4916 | 827 | |||
4917 | 828 | /* place the spinner into the menu item */ | ||
4918 | 829 | window->spinner = gtk_spinner_new (); | ||
4919 | 830 | gtk_container_add (GTK_CONTAINER (item), window->spinner); | ||
4920 | 831 | exo_binding_new (G_OBJECT (window->spinner), "active", | ||
4921 | 832 | G_OBJECT (window->spinner), "visible"); | ||
4922 | 833 | |||
4923 | 834 | /* check if we need to add the root warning */ | ||
4924 | 835 | if (G_UNLIKELY (geteuid () == 0)) | ||
4925 | 836 | { | ||
4926 | 837 | /* add the bar for the root warning */ | ||
4927 | 838 | infobar = gtk_info_bar_new (); | ||
4928 | 839 | gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_WARNING); | ||
4929 | 840 | gtk_table_attach (GTK_TABLE (window->table), infobar, 0, 1, 2, 3, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); | ||
4930 | 841 | gtk_widget_show (infobar); | ||
4931 | 842 | |||
4932 | 843 | /* add the label with the root warning */ | ||
4933 | 844 | label = gtk_label_new (_("Warning, you are using the root account, you may harm your system.")); | ||
4934 | 845 | gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))), label); | ||
4935 | 846 | gtk_widget_show (label); | ||
4936 | 847 | } | ||
4937 | 848 | |||
4938 | 849 | window->paned = gtk_hpaned_new (); | ||
4939 | 850 | gtk_container_set_border_width (GTK_CONTAINER (window->paned), 0); | ||
4940 | 851 | gtk_table_attach (GTK_TABLE (window->table), window->paned, 0, 1, 4, 5, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); | ||
4941 | 852 | gtk_widget_show (window->paned); | ||
4942 | 853 | |||
4943 | 854 | /* determine the last separator position and apply it to the paned view */ | ||
4944 | 855 | gtk_paned_set_position (GTK_PANED (window->paned), last_separator_position); | ||
4945 | 856 | g_signal_connect_swapped (window->paned, "accept-position", G_CALLBACK (thunar_window_save_paned), window); | ||
4946 | 857 | g_signal_connect_swapped (window->paned, "button-release-event", G_CALLBACK (thunar_window_save_paned), window); | ||
4947 | 858 | |||
4948 | 859 | window->view_box = gtk_table_new (3, 1, FALSE); | ||
4949 | 860 | gtk_paned_pack2 (GTK_PANED (window->paned), window->view_box, TRUE, FALSE); | ||
4950 | 861 | gtk_widget_show (window->view_box); | ||
4951 | 862 | |||
4952 | 863 | /* tabs */ | ||
4953 | 864 | window->notebook = gtk_notebook_new (); | ||
4954 | 865 | gtk_table_attach (GTK_TABLE (window->view_box), window->notebook, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); | ||
4955 | 866 | g_signal_connect (G_OBJECT (window->notebook), "switch-page", G_CALLBACK (thunar_window_notebook_switch_page), window); | ||
4956 | 867 | g_signal_connect (G_OBJECT (window->notebook), "page-added", G_CALLBACK (thunar_window_notebook_page_added), window); | ||
4957 | 868 | g_signal_connect (G_OBJECT (window->notebook), "page-removed", G_CALLBACK (thunar_window_notebook_page_removed), window); | ||
4958 | 869 | g_signal_connect_after (G_OBJECT (window->notebook), "button-press-event", G_CALLBACK (thunar_window_notebook_button_press_event), window); | ||
4959 | 870 | g_signal_connect (G_OBJECT (window->notebook), "popup-menu", G_CALLBACK (thunar_window_notebook_popup_menu), window); | ||
4960 | 871 | g_signal_connect (G_OBJECT (window->notebook), "create-window", G_CALLBACK (thunar_window_notebook_create_window), window); | ||
4961 | 872 | gtk_notebook_set_show_border (GTK_NOTEBOOK (window->notebook), FALSE); | ||
4962 | 873 | gtk_notebook_set_homogeneous_tabs (GTK_NOTEBOOK (window->notebook), TRUE); | ||
4963 | 874 | gtk_notebook_set_scrollable (GTK_NOTEBOOK (window->notebook), TRUE); | ||
4964 | 875 | gtk_container_set_border_width (GTK_CONTAINER (window->notebook), 0); | ||
4965 | 876 | gtk_notebook_set_group_name (GTK_NOTEBOOK (window->notebook), "thunar-tabs"); | ||
4966 | 877 | gtk_widget_show (window->notebook); | ||
4967 | 878 | |||
4968 | 879 | /* drop the notebook borders */ | ||
4969 | 880 | style = gtk_rc_style_new (); | ||
4970 | 881 | style->xthickness = style->ythickness = 0; | ||
4971 | 882 | gtk_widget_modify_style (window->notebook, style); | ||
4972 | 883 | g_object_unref (G_OBJECT (style)); | ||
4973 | 884 | |||
4974 | 885 | /* determine the selected location selector */ | ||
4975 | 886 | if (exo_str_is_equal (last_location_bar, g_type_name (THUNAR_TYPE_LOCATION_BUTTONS))) | ||
4976 | 887 | type = THUNAR_TYPE_LOCATION_BUTTONS; | ||
4977 | 888 | else if (exo_str_is_equal (last_location_bar, g_type_name (THUNAR_TYPE_LOCATION_ENTRY))) | ||
4978 | 889 | type = THUNAR_TYPE_LOCATION_ENTRY; | ||
4979 | 890 | else | ||
4980 | 891 | type = G_TYPE_NONE; | ||
4981 | 892 | g_free (last_location_bar); | ||
4982 | 893 | |||
4983 | 894 | /* activate the selected location selector */ | ||
4984 | 895 | action = gtk_action_group_get_action (window->action_group, "view-location-selector-pathbar"); | ||
4985 | 896 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), (type == THUNAR_TYPE_LOCATION_BUTTONS)); | ||
4986 | 897 | action = gtk_action_group_get_action (window->action_group, "view-location-selector-toolbar"); | ||
4987 | 898 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), (type == THUNAR_TYPE_LOCATION_ENTRY)); | ||
4988 | 899 | |||
4989 | 900 | /* determine the selected side pane (FIXME: Should probably be last-shortcuts-visible and last-tree-visible preferences) */ | ||
4990 | 901 | if (exo_str_is_equal (last_side_pane, g_type_name (THUNAR_TYPE_SHORTCUTS_PANE))) | ||
4991 | 902 | type = THUNAR_TYPE_SHORTCUTS_PANE; | ||
4992 | 903 | else if (exo_str_is_equal (last_side_pane, g_type_name (THUNAR_TYPE_TREE_PANE))) | ||
4993 | 904 | type = THUNAR_TYPE_TREE_PANE; | ||
4994 | 905 | else | ||
4995 | 906 | type = G_TYPE_NONE; | ||
4996 | 907 | g_free (last_side_pane); | ||
4997 | 908 | |||
4998 | 909 | /* activate the selected side pane */ | ||
4999 | 910 | action = gtk_action_group_get_action (window->action_group, "view-side-pane-shortcuts"); | ||
5000 | 911 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), (type == THUNAR_TYPE_SHORTCUTS_PANE)); |
The diff has been truncated for viewing.
It'd be really nice if you could explain in d/changelog why the patches are dropped.