UNIXworkcode

1 /* 2 * Copyright 2019 Olaf Wintermann 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 */ 22 23 #include "filedialog.h" 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <ctype.h> 29 #include <sys/stat.h> 30 #include <time.h> 31 #include <dirent.h> 32 #include <fnmatch.h> 33 #include <errno.h> 34 #include <inttypes.h> 35 #include <unistd.h> 36 #include <fcntl.h> 37 38 #include <Xm/PrimitiveP.h> 39 #include <X11/CoreP.h> 40 41 "../Microline/XmL/Grid.h"#include 42 43 #include <X11/Xresource.h> 44 45 46 /* nedit utils */ 47 #include "nedit_malloc.h" 48 #include "utils.h" 49 #include "fileUtils.h" 50 #include "getfiles.h" 51 #include "misc.h" 52 #include "textfield.h" 53 #include "ec_glob.h" 54 #include "pathutils.h" 55 56 #include "../source/preferences.h" 57 #include "../source/filter.h" 58 59 #include "DialogF.h" 60 61 #define WIDGET_SPACING 5 62 #define WINDOW_SPACING 8 63 64 #define BUTTON_EXTRA_SPACE 4 65 66 #define DATE_FORMAT_SAME_YEAR "%b %d %H:%M" 67 #define DATE_FORMAT_OTHER_YEAR "%b %d %Y" 68 69 #define KB_SUFFIX "KiB" 70 #define MB_SUFFIX "MiB" 71 #define GB_SUFFIX "GiB" 72 #define TB_SUFFIX "TiB" 73 74 static int pixmaps_initialized = 0; 75 static int pixmaps_error = 0; 76 77 static Pixmap newFolderIcon16; 78 static Pixmap newFolderIcon24; 79 static Pixmap newFolderIcon32; 80 81 static XColor bgColor; 82 83 static int LastView = -1; // 0: icon 1: list 2: grid 84 static char *LastFilter; 85 86 #define FSB_ENABLE_DETAIL 87 88 const char *newFolder16Data = "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 89 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 90 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 91 "\377\377\377\377\377\377\377\377\377\377>>>\377\064\064\064\377\064\064\064\377" 92 "\064\064\064\377\064\064\064\377ZZZ\377\377\377\377\377\377\377\377\377\377\377" 93 "\377\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\377\377\377\377\377\377" 94 "\377\377\377\377\377\377\064\064\064\377\177\177\177\377\177\177\177\377\177" 95 "\177\177\377www\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\000" 96 "\000\000\377\363\267/\377\363\267/\377\000\000\000\377\064\064\064\377\064\064\064\377\343" 97 "\343\343\377\064\064\064\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377" 98 "ooo\377\000\000\000\377\363\267/\377\363\267/\377\000\000\000\377ooo\377^^^\377;;;\377" 99 "\064\064\064\377ooo\377ooo\377ooo\377ooo\377ooo\377\000\000\000\377\000\000\000\377\000\000" 100 "\000\377\000\000\000\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377\000\000\000\377" 101 "\000\000\000\377\061\061\061\377///\377---\377---\377---\377---\377\000\000\000\377\363" 102 "\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377" 103 "\363\267/\377\363\267/\377\000\000\000\377\061\061\061\377MMM\377MMM\377MMM\377MM" 104 "M\377MMM\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377" 105 "\363\267/\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\064\064\064\377" 106 "MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" 107 "\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\064\064" 108 "\064\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377" 109 "\363\267/\377\363\267/\377\000\000\000\377MMM\377MMM\377\064\064\064\377\064\064\064" 110 "\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\363" 111 "\267/\377\363\267/\377\000\000\000\377MMM\377MMM\377\064\064\064\377\064\064\064\377" 112 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000" 113 "\377\000\000\000\377\000\000\000\377MMM\377MMM\377\064\064\064\377\064\064\064\377MMM\377M" 114 "MM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM" 115 "\377MMM\377MMM\377\064\064\064\377\064\064\064\377MMM\377MMM\377MMM\377MMM\377" 116 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\064" 117 "\064\064\377\064\064\064\377MMM\377MMM\377MMM\377MMM\377KKK\377KKK\377KKK\377" 118 "KKK\377KKK\377JJJ\377JJJ\377GGG\377GGG\377GGG\377\064\064\064\377ZZZ\377\064" 119 "\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 120 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064" 121 "\377\064\064\064\377\064\064\064\377ZZZ\377\377\377\377\377\377\377\377\377\377" 122 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 123 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 124 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 125 "\377"; 126 127 const char *newFolder24Data = "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 128 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 129 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 130 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 131 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 132 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 133 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 134 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 135 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 136 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 137 "\377\377\377\377\377\377\377\377\377\377\377\377EEE\377\064\064\064\377\064\064" 138 "\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\065\065\065\377" 139 "uuu\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 140 "\377\377\377\377\377\377\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" 141 "\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\064" 142 "\064\064\377eee\377\202\202\202\377\202\202\202\377\202\202\202\377\202\202" 143 "\202\377xxx\377fff\377AAA\377uuu\377\354\354\354\377\377\377\377\377\377" 144 "\377\377\377\377\377\377\377\377\377\377\377\000\000\000\377\363\267/\377\363\267" 145 "/\377\363\267/\377\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377" 146 "\377\374\374\374\377\064\064\064\377[[[\377ttt\377ttt\377ttt\377ttt\377ttt\377" 147 "rrr\377eee\377EEE\377\071\071\071\377\071\071\071\377\071\071\071\377\071\071\071\377" 148 "\070\070\070\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377" 149 "\070\070\070\377\064\064\064\377;;;\377\264\264\264\377\064\064\064\377XXX\377ooo" 150 "\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377kkk\377hhh\377hhh\377hhh\377" 151 "hhh\377hhh\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377" 152 "hhh\377hhh\377SSS\377\070\070\070\377\064\064\064\377XXX\377ooo\377ooo\377ooo\377" 153 "ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377\000\000\000\377\000\000\000\377\000\000\000\377" 154 "\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000" 155 "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\064\064\064\377HHH\377VVV\377UUU\377UU" 156 "U\377UUU\377UUU\377UUU\377UUU\377UUU\377UUU\377\000\000\000\377\363\267/\377\363" 157 "\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377" 158 "\363\267/\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377///\377///" 159 "\377...\377...\377...\377...\377...\377...\377...\377...\377...\377\000\000\000" 160 "\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363" 161 "\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377" 162 "\000\000\000\377///\377@@@\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 163 "MMM\377MMM\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\363\267/" 164 "\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363" 165 "\267/\377\363\267/\377\000\000\000\377\064\064\064\377BBB\377MMM\377MMM\377MMM\377" 166 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\000\000\000\377" 167 "\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000" 168 "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\064\064\064\377BBB\377MMM\377MMM\377MM" 169 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 170 "\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377MMM\377MMM\377" 171 "BBB\377\064\064\064\377\064\064\064\377BBB\377MMM\377MMM\377MMM\377MMM\377MMM\377" 172 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\363\267" 173 "/\377\363\267/\377\363\267/\377\000\000\000\377MMM\377MMM\377BBB\377\064\064\064\377" 174 "\064\064\064\377BBB\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM" 175 "\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\363\267/\377\363\267/\377" 176 "\363\267/\377\000\000\000\377MMM\377MMM\377BBB\377\064\064\064\377\064\064\064\377BB" 177 "B\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 178 "MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377M" 179 "MM\377MMM\377BBB\377\064\064\064\377\064\064\064\377BBB\377MMM\377MMM\377MMM\377" 180 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 181 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377BBB\377\064\064\064\377\064\064" 182 "\064\377BBB\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 183 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 184 "MMM\377BBB\377\064\064\064\377\064\064\064\377BBB\377MMM\377MMM\377MMM\377MMM\377" 185 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 186 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377BBB\377\064\064\064\377\064\064\064\377" 187 "BBB\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 188 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 189 "BBB\377\064\064\064\377\064\064\064\377BBB\377MMM\377MMM\377MMM\377MMM\377MMM\377" 190 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 191 "M\377MMM\377MMM\377MMM\377MMM\377BBB\377\064\064\064\377\064\064\064\377BBB\377" 192 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 193 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377KKK\377KKK\377KKK\377KKK\377@@@\377" 194 "\064\064\064\377\064\064\064\377BBB\377MMM\377MMM\377MMM\377MMM\377MMM\377KKK\377" 195 "III\377III\377III\377III\377III\377III\377III\377GGG\377GGG\377GGG\377FF" 196 "F\377FFF\377FFF\377FFF\377>>>\377\064\064\064\377EEE\377\064\064\064\377\064\064" 197 "\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 198 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064" 199 "\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064" 200 "\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377EEE\377\377\377\377\377" 201 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 202 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 203 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 204 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 205 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 206 "\377\377"; 207 208 const char *newFolder32Data = "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 209 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 210 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 211 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 212 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 213 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 214 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 215 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 216 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 217 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 218 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 219 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 220 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 221 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 222 "\377\377\377\377UUU\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 223 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377>>>\377" 224 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 225 "\377\377\377\377\377\377\377\377\377\377\000\000\000\377\000\000\000\377\000\000\000\377\000" 226 "\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\377\377\377\377\377\377" 227 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 228 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064" 229 "\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377>>>" 230 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 231 "\377\377\377\377\377\377\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" 232 "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\377\377\377\377\377\377\377\377\377" 233 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\064\064\064\377" 234 "\064\064\064\377\204\204\204\377\204\204\204\377\204\204\204\377\204\204\204" 235 "\377\204\204\204\377\204\204\204\377yyy\377ooo\377UUU\377\064\064\064\377>>" 236 ">\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 237 "\377\377\377\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\363" 238 "\267/\377\000\000\000\377\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377" 239 "\377\377\377\377\377\377\377\377\377\377\377\377\377\064\064\064\377\064\064\064" 240 "\377yyy\377yyy\377yyy\377yyy\377yyy\377yyy\377yyy\377yyy\377ooo\377UUU\377" 241 "\064\064\064\377>>>\377>>>\377>>>\377>>>\377>>>\377\000\000\000\377\000\000\000\377\363" 242 "\267/\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377;;;\377" 243 ";;;\377\064\064\064\377\064\064\064\377MMM\377\377\377\377\377\064\064\064\377\064" 244 "\064\064\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377" 245 "ooo\377UUU\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064" 246 "\064\377\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\363\267" 247 "/\377\000\000\000\377\000\000\000\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064" 248 "\377\064\064\064\377MMM\377\064\064\064\377\064\064\064\377ooo\377ooo\377ooo\377o" 249 "oo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo" 250 "\377ooo\377ooo\377\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267" 251 "/\377\363\267/\377\000\000\000\377\000\000\000\377ooo\377ooo\377ooo\377ooo\377\064\064" 252 "\064\377\064\064\064\377\064\064\064\377\064\064\064\377ooo\377ooo\377ooo\377ooo\377" 253 "ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377\000\000\000\377\000\000\000\377\000\000\000\377" 254 "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\363\267/\377\363\267/" 255 "\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000" 256 "\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\064\064\064\377\064\064\064\377ooo\377o" 257 "oo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377ooo\377\000\000\000\377" 258 "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\363" 259 "\267/\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377\000\000" 260 "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\064\064\064\377\064" 261 "\064\064\377\064\064\064\377+++\377+++\377+++\377+++\377+++\377+++\377+++\377" 262 "+++\377+++\377\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377" 263 "\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267" 264 "/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363" 265 "\267/\377\363\267/\377\000\000\000\377\000\000\000\377...\377...\377...\377...\377.." 266 ".\377...\377...\377...\377...\377...\377...\377...\377\000\000\000\377\000\000\000\377" 267 "\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267" 268 "/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363" 269 "\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000" 270 "\000\000\377...\377...\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377M" 271 "MM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267" 272 "/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363" 273 "\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377" 274 "\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377\064\064\064\377\064\064\064\377" 275 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000" 276 "\000\000\377\000\000\000\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363" 277 "\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377" 278 "\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267/\377\363\267" 279 "/\377\000\000\000\377\000\000\000\377\064\064\064\377\064\064\064\377MMM\377MMM\377MMM\377" 280 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\000\000" 281 "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\363\267/\377\363" 282 "\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" 283 "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\064\064\064\377\064\064\064\377MMM\377" 284 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377" 285 "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\363" 286 "\267/\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377\000\000" 287 "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\064\064\064\377\064" 288 "\064\064\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 289 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\363" 290 "\267/\377\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377MMM\377" 291 "MMM\377MMM\377MMM\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 292 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 293 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\363\267/\377" 294 "\363\267/\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377MMM\377MMM\377" 295 "MMM\377MMM\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377MMM\377" 296 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 297 "M\377MMM\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\363\267/\377\363\267" 298 "/\377\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377MMM\377MMM\377MMM\377" 299 "MMM\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377MMM\377MMM\377" 300 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 301 "M\377MMM\377MMM\377MMM\377\000\000\000\377\000\000\000\377\363\267/\377\363\267/\377" 302 "\363\267/\377\363\267/\377\000\000\000\377\000\000\000\377MMM\377MMM\377MMM\377MMM\377" 303 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377MMM\377MMM\377MMM\377" 304 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 305 "M\377MMM\377MMM\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" 306 "\000\377\000\000\000\377\000\000\000\377MMM\377MMM\377MMM\377MMM\377\064\064\064\377\064\064" 307 "\064\377\064\064\064\377\064\064\064\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 308 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\000" 309 "\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" 310 "\000\377MMM\377MMM\377MMM\377MMM\377\064\064\064\377\064\064\064\377\064\064\064\377" 311 "\064\064\064\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM" 312 "\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 313 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\064\064\064" 314 "\377\064\064\064\377\064\064\064\377\064\064\064\377MMM\377MMM\377MMM\377MMM\377M" 315 "MM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM" 316 "\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 317 "MMM\377MMM\377MMM\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 318 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 319 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 320 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377\064\064\064\377\064\064\064\377" 321 "\064\064\064\377\064\064\064\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 322 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 323 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377" 324 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377MMM\377MMM\377MMM\377" 325 "MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MM" 326 "M\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377HHH\377HHH\377" 327 "HHH\377HHH\377HHH\377HHH\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064" 328 "\064\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377MMM\377HHH\377HH" 329 "H\377HHH\377HHH\377HHH\377HHH\377HHH\377HHH\377HHH\377HHH\377FFF\377FFF\377" 330 "FFF\377FFF\377FFF\377FFF\377FFF\377FFF\377FFF\377FFF\377\064\064\064\377\064" 331 "\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 332 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064" 333 "\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064" 334 "\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 335 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064" 336 "\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377UUU\377\064\064\064" 337 "\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064" 338 "\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 339 "\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064" 340 "\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064" 341 "\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377\064\064\064\377" 342 "\064\064\064\377UUU\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 343 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 344 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 345 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 346 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 347 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 348 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 349 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 350 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 351 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 352 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 353 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 354 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 355 "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 356 "\377\377\377\377\377\377\377\377\377"; 357 358 typedef struct FileDialogData FileDialogData; 359 360 static void FileListDetailSelect(FileDialogData *fsb, const char *item); 361 static void FileListSelect(FileDialogData *fsb, const char *item); 362 static void FileIconViewSelect(FileDialogData *fsb, const char *item); 363 364 static void FileSelect(FileDialogData *fsb, const char *item); 365 366 static void select_view(FileDialogData *data); 367 368 static void filedialog_update_dir(FileDialogData *data, char *path); 369 370 static void filedialog_ok(Widget w, FileDialogData *data, XtPointer d); 371 372 /* 373 * get number of shifts for specific color mask 374 */ 375 static int get_shift(unsigned long mask) { 376 if(mask == 0) { 377 return 0; 378 } 379 380 int shift = 0; 381 while((mask & 1L) == 0) { 382 shift++; 383 mask >>= 1; 384 } 385 return shift; 386 } 387 388 /* 389 * get number of bits from color mask 390 */ 391 static int get_mask_len(unsigned long mask) { 392 if(mask == 0) { 393 return 0; 394 } 395 396 while((mask & 1L) == 0) { 397 mask >>= 1; 398 } 399 int len = 0; 400 while((mask & 1L) == 1) { 401 len++; 402 mask >>= 1; 403 } 404 return len; 405 } 406 407 /* 408 * create an image from data and copy image to the specific pixmap 409 */ 410 static void create_image(Display *dp, Visual *visual, int depth, Pixmap pix, const char *data, int wh) { 411 // wh is width and height 412 size_t imglen = wh*wh*4; 413 char *imgdata = malloc(imglen); // will be freed with XDestroyImage 414 415 //get number of shifts required for each color bit mask 416 int red_shift = get_shift(visual->red_mask); 417 int green_shift = get_shift(visual->green_mask); 418 int blue_shift = get_shift(visual->blue_mask); 419 420 uint32_t *src = (uint32_t*)data; 421 uint32_t *dst = (uint32_t*)imgdata; 422 // init all bits that are not in any mask 423 uint32_t pixel_init = UINT32_MAX ^ (visual->red_mask ^ visual->green_mask ^ visual->blue_mask); 424 425 // convert pixels to visual-specific format 426 size_t len = wh*wh; 427 for(int i=0;i<len;i++) { 428 uint32_t pixel = src[i]; 429 #if defined(__sparc) || defined(__sparc__) 430 // big endian: sparc is probably the only relevant platform 431 //uint8_t alpha = pixel & 0xFF; 432 uint8_t blue = (pixel & 0xFF00) >> 8; 433 uint8_t green = (pixel & 0xFF0000) >> 16; 434 uint8_t red = (pixel & 0xFF000000) >> 24; 435 #else 436 // little endian: basically all modern cpus 437 uint8_t red = pixel & 0xFF; 438 uint8_t green = (pixel & 0xFF00) >> 8; 439 uint8_t blue = (pixel & 0xFF0000) >> 16; 440 #endif 441 // TODO: work with alpha value 442 if(pixel == UINT32_MAX) { 443 red = bgColor.red; 444 green = bgColor.green; 445 blue = bgColor.blue; 446 } 447 448 uint32_t out = pixel_init; 449 out ^= (red << red_shift); 450 out ^= (green << green_shift); 451 out ^= (blue << blue_shift); 452 dst[i] = out; 453 } 454 455 // create rgb image (32 bit alignment) 456 XImage *img = XCreateImage(dp, visual, depth, ZPixmap, 0, imgdata, wh, wh, 32, 0); 457 458 XGCValues gcval; 459 gcval.graphics_exposures = 0; 460 unsigned long valuemask = GCGraphicsExposures; 461 GC gc = XCreateGC(dp, newFolderIcon16, valuemask, &gcval); 462 463 XPutImage(dp, pix, gc, img, 0, 0, 0, 0, wh, wh); 464 465 XDestroyImage(img); 466 XFreeGC(dp, gc); 467 } 468 469 static void initPixmaps(Display *dp, Drawable d, Screen *screen, int depth) 470 { 471 // get the correct visual for current screen/depth 472 Visual *visual = NULL; 473 for(int i=0;i<screen->ndepths;i++) { 474 Depth d = screen->depths[i]; 475 if(d.depth == depth) { 476 for(int v=0;v<d.nvisuals;v++) { 477 Visual *vs = &d.visuals[v]; 478 if(get_mask_len(vs->red_mask) == 8) { 479 visual = vs; 480 break; 481 } 482 } 483 } 484 } 485 486 if(!visual) { 487 // no visual found for using rgb images 488 fprintf(stderr, "can''t use images with this visual\n"); 489 pixmaps_initialized = 1; 490 pixmaps_error = 1; 491 return; 492 } 493 494 // create pixmaps and load images 495 newFolderIcon16 = XCreatePixmap(dp, d, 16, 16, depth); 496 if(newFolderIcon16 == BadValue) { 497 fprintf(stderr, "failed to create newFolderIcon16 pixmap\n"); 498 pixmaps_error = 1; 499 } else { 500 create_image(dp, visual, depth, newFolderIcon16, newFolder16Data, 16); 501 } 502 503 newFolderIcon24 = XCreatePixmap(dp, d, 24, 24, depth); 504 if(newFolderIcon24 == BadValue) { 505 fprintf(stderr, "failed to create newFolderIcon24 pixmap\n"); 506 pixmaps_error = 1; 507 } else { 508 create_image(dp, visual, depth, newFolderIcon24, newFolder24Data, 24); 509 } 510 511 newFolderIcon32 = XCreatePixmap(dp, d, 32, 32, depth); 512 if(newFolderIcon32 == BadValue) { 513 fprintf(stderr, "failed to create newFolderIcon32 pixmap\n"); 514 pixmaps_error = 1; 515 } else { 516 create_image(dp, visual, depth, newFolderIcon32, newFolder32Data, 32); 517 } 518 519 pixmaps_initialized = 1; 520 } 521 522 523 /* -------------------- path bar -------------------- */ 524 525 typedef void(*updatedir_callback)(void*,char*); 526 527 typedef struct PathBar { 528 Widget widget; 529 Widget textfield; 530 531 Widget focus_widget; 532 533 Widget left; 534 Widget right; 535 Dimension lw; 536 Dimension rw; 537 538 int shift; 539 540 Widget *pathSegments; 541 size_t numSegments; 542 size_t segmentAlloc; 543 544 char *path; 545 int selection; 546 Boolean input; 547 548 int focus; 549 550 updatedir_callback updateDir; 551 void *updateDirData; 552 } PathBar; 553 554 void PathBarSetPath(PathBar *bar, char *path); 555 556 void pathbar_resize(Widget w, PathBar *p, XtPointer d) 557 { 558 Dimension width, height; 559 XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); 560 561 Dimension *segW = NEditCalloc(p->numSegments, sizeof(Dimension)); 562 563 Dimension maxHeight = 0; 564 565 /* get width/height from all widgets */ 566 Dimension pathWidth = 0; 567 for(int i=0;i<p->numSegments;i++) { 568 Dimension segWidth; 569 Dimension segHeight; 570 XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); 571 segW[i] = segWidth; 572 pathWidth += segWidth; 573 if(segHeight > maxHeight) { 574 maxHeight = segHeight; 575 } 576 } 577 Dimension tfHeight; 578 XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); 579 if(tfHeight > maxHeight) { 580 maxHeight = tfHeight; 581 } 582 583 Boolean arrows = False; 584 if(pathWidth + 10 > width) { 585 arrows = True; 586 pathWidth += p->lw + p->rw; 587 } 588 589 /* calc max visible widgets */ 590 int start = 0; 591 if(arrows) { 592 Dimension vis = p->lw+p->rw; 593 for(int i=p->numSegments;i>0;i--) { 594 Dimension segWidth = segW[i-1]; 595 if(vis + segWidth + 10 > width) { 596 start = i; 597 arrows = True; 598 break; 599 } 600 vis += segWidth; 601 } 602 } else { 603 p->shift = 0; 604 } 605 606 int leftShift = 0; 607 if(p->shift < 0) { 608 if(start + p->shift < 0) { 609 leftShift = start; 610 start = 0; 611 p->shift = -leftShift; 612 } else { 613 leftShift = -p->shift; /* negative shift */ 614 start += p->shift; 615 } 616 } 617 618 int x = 0; 619 if(arrows) { 620 XtManageChild(p->left); 621 XtManageChild(p->right); 622 x = p->lw; 623 } else { 624 XtUnmanageChild(p->left); 625 XtUnmanageChild(p->right); 626 } 627 628 for(int i=0;i<p->numSegments;i++) { 629 if(i >= start && i < p->numSegments - leftShift && !p->input) { 630 XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); 631 x += segW[i]; 632 XtManageChild(p->pathSegments[i]); 633 } else { 634 XtUnmanageChild(p->pathSegments[i]); 635 } 636 } 637 638 if(arrows) { 639 XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); 640 XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); 641 } 642 643 NEditFree(segW); 644 645 Dimension rw, rh; 646 XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); 647 648 XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL); 649 } 650 651 static void pathbarActivateTF(PathBar *p) 652 { 653 XtUnmanageChild(p->left); 654 XtUnmanageChild(p->right); 655 XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0); 656 XtManageChild(p->textfield); 657 p->input = 1; 658 659 XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); 660 661 pathbar_resize(p->widget, p, NULL); 662 } 663 664 void PathBarActivateTextfield(PathBar *p) 665 { 666 p->focus = 1; 667 pathbarActivateTF(p); 668 } 669 670 void pathbar_input(Widget w, PathBar *p, XtPointer c) 671 { 672 XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; 673 XEvent *xevent = cbs->event; 674 675 if (cbs->reason == XmCR_INPUT) { 676 if (xevent->xany.type == ButtonPress) { 677 p->focus = 0; 678 pathbarActivateTF(p); 679 } 680 } 681 } 682 683 void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) 684 { 685 if(--p->focus < 0) { 686 p->input = False; 687 XtUnmanageChild(p->textfield); 688 } 689 } 690 691 void pathbar_pathinput(Widget w, PathBar *p, XtPointer d) 692 { 693 char *newpath = XNETextGetString(p->textfield); 694 if(newpath) { 695 if(newpath[0] == '~') { 696 char *p = newpath+1; 697 char *cp = ConcatPath(GetHomeDir(), p); 698 XtFree(newpath); 699 newpath = cp; 700 } else if(newpath[0] != '/') { 701 char *cp = ConcatPath(GetCurrentDir(), newpath); 702 XtFree(newpath); 703 newpath = cp; 704 } 705 706 /* update path */ 707 PathBarSetPath(p, newpath); 708 if(p->updateDir) { 709 p->updateDir(p->updateDirData, newpath); 710 } 711 XtFree(newpath); 712 713 /* hide textfield and show path as buttons */ 714 XtUnmanageChild(p->textfield); 715 pathbar_resize(p->widget, p, NULL); 716 717 if(p->focus_widget) { 718 XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT); 719 } 720 } 721 } 722 723 void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) 724 { 725 p->shift--; 726 pathbar_resize(p->widget, p, NULL); 727 } 728 729 void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) 730 { 731 if(p->shift < 0) { 732 p->shift++; 733 } 734 pathbar_resize(p->widget, p, NULL); 735 } 736 737 static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { 738 PathBar *pb = data; 739 if(event->type == KeyReleaseMask) { 740 if(event->xkey.keycode == 9) { 741 XtUnmanageChild(pb->textfield); 742 pathbar_resize(pb->widget, pb, NULL); 743 *dispatch = False; 744 } else if(event->xkey.keycode == 36) { 745 pathbar_pathinput(pb->textfield, pb, NULL); 746 *dispatch = False; 747 } 748 } 749 } 750 751 PathBar* CreatePathBar(Widget parent, ArgList args, int n) 752 { 753 PathBar *bar = NEditMalloc(sizeof(PathBar)); 754 bar->path = NULL; 755 bar->updateDir = NULL; 756 bar->updateDirData = NULL; 757 758 bar->focus_widget = NULL; 759 760 bar->shift = 0; 761 762 XtSetArg(args[n], XmNmarginWidth, 0); n++; 763 XtSetArg(args[n], XmNmarginHeight, 0); n++; 764 bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); 765 XtAddCallback( 766 bar->widget, 767 XmNresizeCallback, 768 (XtCallbackProc)pathbar_resize, 769 bar); 770 XtAddCallback( 771 bar->widget, 772 XmNinputCallback, 773 (XtCallbackProc)pathbar_input, 774 bar); 775 776 Arg a[4]; 777 XtSetArg(a[0], XmNshadowThickness, 0); 778 XtSetArg(a[1], XmNx, 0); 779 XtSetArg(a[2], XmNy, 0); 780 bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3); 781 bar->input = 0; 782 XtAddCallback( 783 bar->textfield, 784 XmNlosingFocusCallback, 785 (XtCallbackProc)pathbar_losingfocus, 786 bar); 787 XtAddCallback(bar->textfield, XmNactivateCallback, 788 (XtCallbackProc)pathbar_pathinput, bar); 789 XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); 790 791 XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); 792 bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); 793 XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); 794 bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); 795 XtAddCallback( 796 bar->left, 797 XmNactivateCallback, 798 (XtCallbackProc)pathbar_shift_left, 799 bar); 800 XtAddCallback( 801 bar->right, 802 XmNactivateCallback, 803 (XtCallbackProc)pathbar_shift_right, 804 bar); 805 806 Pixel bg; 807 XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); 808 XtVaSetValues(bar->widget, XmNbackground, bg, NULL); 809 810 XtManageChild(bar->left); 811 XtManageChild(bar->right); 812 813 XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); 814 XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); 815 816 bar->segmentAlloc = 16; 817 bar->numSegments = 0; 818 bar->pathSegments = NEditCalloc(16, sizeof(Widget)); 819 820 bar->selection = 0; 821 822 return bar; 823 } 824 825 void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c) 826 { 827 XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); 828 829 for(int i=0;i<bar->numSegments;i++) { 830 if(bar->pathSegments[i] == w) { 831 bar->selection = i; 832 XmToggleButtonSetState(w, True, False); 833 break; 834 } 835 } 836 837 int plen = strlen(bar->path); 838 int countSeg = 0; 839 for(int i=0;i<=plen;i++) { 840 char c = bar->path[i]; 841 if(c == '/' || c == '\0') { 842 if(countSeg == bar->selection) { 843 char *dir = NEditMalloc(i+2); 844 memcpy(dir, bar->path, i+1); 845 dir[i+1] = '\0'; 846 if(bar->updateDir) { 847 bar->updateDir(bar->updateDirData, dir); 848 } 849 NEditFree(dir); 850 } 851 countSeg++; 852 } 853 } 854 } 855 856 void PathBarSetPath(PathBar *bar, char *path) 857 { 858 if(bar->path) { 859 NEditFree(bar->path); 860 } 861 bar->path = NEditStrdup(path); 862 863 for(int i=0;i<bar->numSegments;i++) { 864 XtDestroyWidget(bar->pathSegments[i]); 865 } 866 XtUnmanageChild(bar->textfield); 867 XtManageChild(bar->left); 868 XtManageChild(bar->right); 869 bar->input = False; 870 871 Arg args[4]; 872 XmString str; 873 874 bar->numSegments = 0; 875 876 int i=0; 877 if(path[0] == '/') { 878 str = XmStringCreateLocalized("/"); 879 XtSetArg(args[0], XmNlabelString, str); 880 XtSetArg(args[1], XmNfillOnSelect, True); 881 XtSetArg(args[2], XmNindicatorOn, False); 882 bar->pathSegments[0] = XmCreateToggleButton( 883 bar->widget, "pbbutton", args, 3); 884 XtAddCallback( 885 bar->pathSegments[0], 886 XmNvalueChangedCallback, 887 (XtCallbackProc)PathBarChangeDir, 888 bar); 889 XmStringFree(str); 890 bar->numSegments++; 891 i++; 892 } 893 894 int len = strlen(path); 895 int begin = i; 896 for(;i<=len;i++) { 897 char c = path[i]; 898 if((c == '/' || c == '\0') && i > begin) { 899 char *segStr = NEditMalloc(i - begin + 1); 900 memcpy(segStr, path+begin, i-begin); 901 segStr[i-begin] = '\0'; 902 begin = i+1; 903 904 str = XmStringCreateLocalized(segStr); 905 NEditFree(segStr); 906 XtSetArg(args[0], XmNlabelString, str); 907 XtSetArg(args[1], XmNfillOnSelect, True); 908 XtSetArg(args[2], XmNindicatorOn, False); 909 Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3); 910 XtAddCallback( 911 button, 912 XmNvalueChangedCallback, 913 (XtCallbackProc)PathBarChangeDir, 914 bar); 915 XmStringFree(str); 916 917 if(bar->numSegments >= bar->segmentAlloc) { 918 bar->segmentAlloc += 8; 919 bar->pathSegments = realloc(bar->pathSegments, bar->segmentAlloc * sizeof(Widget)); 920 } 921 922 bar->pathSegments[bar->numSegments++] = button; 923 } 924 } 925 926 bar->selection = bar->numSegments-1; 927 XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); 928 929 XNETextSetString(bar->textfield, path); 930 XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield)); 931 932 pathbar_resize(bar->widget, bar, NULL); 933 } 934 935 void PathBarDestroy(PathBar *pathbar) { 936 if(pathbar->path) { 937 NEditFree(pathbar->path); 938 } 939 NEditFree(pathbar->pathSegments); 940 NEditFree(pathbar); 941 } 942 943 944 945 /* -------------------- file dialog -------------------- */ 946 947 typedef struct FileElm FileElm; 948 struct FileElm { 949 char *path; 950 int isDirectory; 951 uint64_t size; 952 time_t lastModified; 953 }; 954 955 struct FileDialogData { 956 Widget shell; 957 958 char *file_path; 959 960 Widget path; 961 PathBar *pathBar; 962 Widget filter; 963 964 int selectedview; 965 966 // dir/file list view 967 Widget listform; 968 Widget dirlist; 969 Widget filelist; 970 Widget filelistcontainer; 971 972 // detail view 973 Widget grid; 974 Widget gridcontainer; 975 976 Widget okBtn; 977 Widget name; 978 Widget wrap; 979 Widget unixFormat; 980 Widget dosFormat; 981 Widget macFormat; 982 Widget encoding; 983 Widget iofilter; 984 Widget bom; 985 Widget xattr; 986 987 FileElm *dirs; 988 FileElm *files; 989 int dircount; 990 int filecount; 991 int maxnamelen; 992 993 int gridRealized; 994 995 char *currentPath; 996 char *selectedPath; 997 int selIsDir; 998 int showHidden; 999 1000 IOFilter **filters; 1001 size_t nfilters; 1002 IOFilter *selected_filter; 1003 1004 int type; 1005 1006 int end; 1007 int status; 1008 }; 1009 1010 static void filedialog_cancel(Widget w, FileDialogData *data, XtPointer d) 1011 { 1012 data->end = 1; 1013 data->status = FILEDIALOG_CANCEL; 1014 } 1015 1016 static void cleanupLists(FileDialogData *data) 1017 { 1018 XmListDeleteAllItems(data->dirlist); 1019 XmListDeleteAllItems(data->filelist); 1020 } 1021 1022 static void filedialog_check_iofilters(FileDialogData *data, const char *path) 1023 { 1024 if(!path || !data->iofilter) { 1025 return; 1026 } 1027 1028 for(int i=0;i<data->nfilters;i++) { 1029 if(data->filters[i]->ec_pattern) { 1030 if(!ec_glob(data->filters[i]->ec_pattern, path)) { 1031 XtVaSetValues(data->iofilter, XmNselectedPosition, i+1, NULL); 1032 return; 1033 } 1034 } 1035 } 1036 1037 XtVaSetValues(data->iofilter, XmNselectedPosition, 0, NULL); 1038 } 1039 1040 /* 1041 * file_cmp_field 1042 * 0: compare path 1043 * 1: compare size 1044 * 2: compare mtime 1045 */ 1046 static int file_cmp_field = 0; 1047 1048 /* 1049 * 1 or -1 1050 */ 1051 static int file_cmp_order = 1; 1052 1053 static int filecmp(const void *f1, const void *f2) 1054 { 1055 const FileElm *file1 = f1; 1056 const FileElm *file2 = f2; 1057 if(file1->isDirectory != file2->isDirectory) { 1058 return file1->isDirectory < file2->isDirectory; 1059 } 1060 1061 int cmp_field = file_cmp_field; 1062 int cmp_order = file_cmp_order; 1063 if(file1->isDirectory) { 1064 cmp_field = 0; 1065 cmp_order = 1; 1066 } 1067 1068 int ret = 0; 1069 switch(cmp_field) { 1070 case 0: { 1071 ret = strcmp(FileName(file1->path), FileName(file2->path)); 1072 break; 1073 } 1074 case 1: { 1075 if(file1->size < file2->size) { 1076 ret = -1; 1077 } else if(file1->size == file2->size) { 1078 ret = 0; 1079 } else { 1080 ret = 1; 1081 } 1082 break; 1083 } 1084 case 2: { 1085 if(file1->lastModified < file2->lastModified) { 1086 ret = -1; 1087 } else if(file1->lastModified == file2->lastModified) { 1088 ret = 0; 1089 } else { 1090 ret = 1; 1091 } 1092 break; 1093 } 1094 } 1095 1096 return ret * cmp_order; 1097 } 1098 1099 typedef void(*ViewUpdateFunc)(FileDialogData*,FileElm*,FileElm*,int,int,int); 1100 1101 1102 static void filelistwidget_add(Widget w, int showHidden, char *filter, FileElm *ls, int count) 1103 { 1104 if(count > 0) { 1105 XmStringTable items = NEditCalloc(count, sizeof(XmString)); 1106 int i = 0; 1107 1108 for(int j=0;j<count;j++) { 1109 FileElm *e = &ls[j]; 1110 1111 char *name = FileName(e->path); 1112 if((!showHidden && name[0] == '.') || fnmatch(filter, name, 0)) { 1113 continue; 1114 } 1115 1116 items[i] = XmStringCreateLocalized(name); 1117 i++; 1118 } 1119 XmListAddItems(w, items, i, 0); 1120 for(int i=0;i<count;i++) { 1121 XmStringFree(items[i]); 1122 } 1123 NEditFree(items); 1124 } 1125 } 1126 1127 1128 static void filedialog_update_lists( 1129 FileDialogData *data, 1130 FileElm *dirs, 1131 FileElm *files, 1132 int dircount, 1133 int filecount, 1134 int maxnamelen) 1135 { 1136 char *filter = XNETextGetString(data->filter); 1137 char *filterStr = filter; 1138 if(!filter || strlen(filter) == 0) { 1139 filterStr = "*"; 1140 } 1141 1142 filelistwidget_add(data->dirlist, data->showHidden, "*", dirs, dircount); 1143 filelistwidget_add(data->filelist, data->showHidden, filterStr, files, filecount); 1144 1145 if(filter) { 1146 XtFree(filter); 1147 } 1148 } 1149 1150 /* 1151 * create file size string with kb/mb/gb/tb suffix 1152 */ 1153 static char* size_str(FileElm *f) { 1154 char *str = malloc(16); 1155 uint64_t size = f->size; 1156 1157 if(f->isDirectory) { 1158 str[0] = '\0'; 1159 } else if(size < 0x400) { 1160 snprintf(str, 16, "%d bytes", (int)size); 1161 } else if(size < 0x100000) { 1162 float s = (float)size/0x400; 1163 int diff = (s*100 - (int)s*100); 1164 if(diff > 90) { 1165 diff = 0; 1166 s += 0.10f; 1167 } 1168 if(size < 0x2800 && diff != 0) { 1169 // size < 10 KiB 1170 snprintf(str, 16, "%.1f " KB_SUFFIX, s); 1171 } else { 1172 snprintf(str, 16, "%.0f " KB_SUFFIX, s); 1173 } 1174 } else if(size < 0x40000000) { 1175 float s = (float)size/0x100000; 1176 int diff = (s*100 - (int)s*100); 1177 if(diff > 90) { 1178 diff = 0; 1179 s += 0.10f; 1180 } 1181 if(size < 0xa00000 && diff != 0) { 1182 // size < 10 MiB 1183 snprintf(str, 16, "%.1f " MB_SUFFIX, s); 1184 } else { 1185 size /= 0x100000; 1186 snprintf(str, 16, "%.0f " MB_SUFFIX, s); 1187 } 1188 } else if(size < 0x1000000000ULL) { 1189 float s = (float)size/0x40000000; 1190 int diff = (s*100 - (int)s*100); 1191 if(diff > 90) { 1192 diff = 0; 1193 s += 0.10f; 1194 } 1195 if(size < 0x280000000 && diff != 0) { 1196 // size < 10 GiB 1197 snprintf(str, 16, "%.1f " GB_SUFFIX, s); 1198 } else { 1199 size /= 0x40000000; 1200 snprintf(str, 16, "%.0f " GB_SUFFIX, s); 1201 } 1202 } else { 1203 size /= 1024; 1204 float s = (float)size/0x40000000; 1205 int diff = (s*100 - (int)s*100); 1206 if(diff > 90) { 1207 diff = 0; 1208 s += 0.10f; 1209 } 1210 if(size < 0x280000000 && diff != 0) { 1211 // size < 10 TiB 1212 snprintf(str, 16, "%.1f " TB_SUFFIX, s); 1213 } else { 1214 size /= 0x40000000; 1215 snprintf(str, 16, "%.0f " TB_SUFFIX, s); 1216 } 1217 } 1218 return str; 1219 } 1220 1221 static char* date_str(time_t tm) { 1222 struct tm t; 1223 struct tm n; 1224 time_t now = time(NULL); 1225 1226 localtime_r(&tm, &t); 1227 localtime_r(&now, &n); 1228 1229 char *str = malloc(16); 1230 if(t.tm_year == n.tm_year) { 1231 strftime(str, 16, DATE_FORMAT_SAME_YEAR, &t); 1232 } else { 1233 strftime(str, 16, DATE_FORMAT_OTHER_YEAR, &t); 1234 } 1235 return str; 1236 } 1237 1238 static void filegridwidget_add(Widget grid, int showHidden, char *filter, FileElm *ls, int count, int maxWidth) 1239 { 1240 XmLGridAddRows(grid, XmCONTENT, 1, count); 1241 1242 int row = 0; 1243 for(int i=0;i<count;i++) { 1244 FileElm *e = &ls[i]; 1245 1246 char *name = FileName(e->path); 1247 if((!showHidden && name[0] == '.') || (!e->isDirectory && fnmatch(filter, name, 0))) { 1248 continue; 1249 } 1250 1251 // name 1252 XmString str = XmStringCreateLocalized(name); 1253 XtVaSetValues(grid, 1254 XmNcolumn, 0, 1255 XmNrow, row, 1256 XmNcellString, str, NULL); 1257 XmStringFree(str); 1258 // size 1259 char *szbuf = size_str(e); 1260 str = XmStringCreateLocalized(szbuf); 1261 XtVaSetValues(grid, 1262 XmNcolumn, 1, 1263 XmNrow, row, 1264 XmNcellString, str, NULL); 1265 free(szbuf); 1266 XmStringFree(str); 1267 // date 1268 char *datebuf = date_str(e->lastModified); 1269 str = XmStringCreateLocalized(datebuf); 1270 XtVaSetValues(grid, 1271 XmNcolumn, 2, 1272 XmNrow, row, 1273 XmNcellString, str, NULL); 1274 free(datebuf); 1275 XmStringFree(str); 1276 1277 XtVaSetValues(grid, XmNrow, row, XmNrowUserData, e, NULL); 1278 row++; 1279 } 1280 1281 // remove unused rows 1282 if(count > row) { 1283 XmLGridDeleteRows(grid, XmCONTENT, row, count-row); 1284 } 1285 1286 if(maxWidth < 16) { 1287 maxWidth = 16; 1288 } 1289 1290 XtVaSetValues(grid, 1291 XmNcolumnRangeStart, 0, 1292 XmNcolumnRangeEnd, 0, 1293 XmNcolumnWidth, maxWidth, 1294 XmNcellAlignment, XmALIGNMENT_LEFT, 1295 XmNcolumnSizePolicy, XmVARIABLE, 1296 NULL); 1297 XtVaSetValues(grid, 1298 XmNcolumnRangeStart, 1, 1299 XmNcolumnRangeEnd, 1, 1300 XmNcolumnWidth, 9, 1301 XmNcellAlignment, XmALIGNMENT_LEFT, 1302 XmNcolumnSizePolicy, XmVARIABLE, 1303 NULL); 1304 XtVaSetValues(grid, 1305 XmNcolumnRangeStart, 2, 1306 XmNcolumnRangeEnd, 2, 1307 XmNcolumnWidth, 16, 1308 XmNcellAlignment, XmALIGNMENT_RIGHT, 1309 XmNcolumnSizePolicy, XmVARIABLE, 1310 NULL); 1311 1312 XmLGridColumn column0 = XmLGridGetColumn(grid, XmCONTENT, 1); 1313 XmLGridColumn column1 = XmLGridGetColumn(grid, XmCONTENT, 1); 1314 XmLGridColumn column2 = XmLGridGetColumn(grid, XmCONTENT, 2); 1315 1316 Dimension col0Width = XmLGridColumnWidthInPixels(column0); 1317 Dimension col1Width = XmLGridColumnWidthInPixels(column1); 1318 Dimension col2Width = XmLGridColumnWidthInPixels(column2); 1319 1320 Dimension totalWidth = col0Width + col1Width + col2Width; 1321 1322 Dimension gridWidth = 0; 1323 Dimension gridShadow = 0; 1324 XtVaGetValues(grid, XmNwidth, &gridWidth, XmNshadowThickness, &gridShadow, NULL); 1325 1326 Dimension widthDiff = gridWidth - totalWidth - gridShadow - gridShadow; 1327 1328 if(gridWidth > totalWidth) { 1329 XtVaSetValues(grid, 1330 XmNcolumnRangeStart, 0, 1331 XmNcolumnRangeEnd, 0, 1332 XmNcolumnWidth, col0Width + widthDiff - XmLGridVSBWidth(grid) - 2, 1333 XmNcolumnSizePolicy, XmCONSTANT, 1334 NULL); 1335 } 1336 } 1337 1338 static void filedialog_update_grid( 1339 FileDialogData *data, 1340 FileElm *dirs, 1341 FileElm *files, 1342 int dircount, 1343 int filecount, 1344 int maxnamelen) 1345 { 1346 char *filter = XNETextGetString(data->filter); 1347 char *filterStr = filter; 1348 if(!filter || strlen(filter) == 0) { 1349 filterStr = "*"; 1350 } 1351 1352 // update dir list 1353 filelistwidget_add(data->dirlist, data->showHidden, "*", dirs, dircount); 1354 // update file detail grid 1355 filegridwidget_add(data->grid, data->showHidden, filterStr, files, filecount, maxnamelen); 1356 1357 if(filter) { 1358 XtFree(filter); 1359 } 1360 } 1361 1362 static void cleanupGrid(FileDialogData *data) 1363 { 1364 // cleanup dir list widget 1365 XmListDeleteAllItems(data->dirlist); 1366 1367 // cleanup grid 1368 Cardinal rows = 0; 1369 XtVaGetValues(data->grid, XmNrows, &rows, NULL); 1370 XmLGridDeleteRows(data->grid, XmCONTENT, 0, rows); 1371 } 1372 1373 1374 static void free_files(FileElm *ls, int count) 1375 { 1376 for(int i=0;i<count;i++) { 1377 if(ls[i].path) { 1378 free(ls[i].path); 1379 } 1380 } 1381 free(ls); 1382 } 1383 1384 static void filedialog_cleanup_filedata(FileDialogData *data) 1385 { 1386 if(data->dirs) { 1387 free_files(data->dirs, data->dircount); 1388 } 1389 if(data->files) { 1390 free_files(data->files, data->filecount); 1391 } 1392 data->dirs = NULL; 1393 data->files = NULL; 1394 data->dircount = 0; 1395 data->filecount = 0; 1396 data->maxnamelen = 0; 1397 } 1398 1399 1400 // ported from motifextfsb 1401 static void FileListDetailSelect(FileDialogData *fsb, const char *item) { 1402 int numRows = 0; 1403 XtVaGetValues(fsb->grid, XmNrows, &numRows, NULL); 1404 1405 XmLGridColumn col = XmLGridGetColumn(fsb->grid, XmCONTENT, 0); 1406 for(int i=0;i<numRows;i++) { 1407 XmLGridRow row = XmLGridGetRow(fsb->grid, XmCONTENT, i); 1408 FileElm *elm = NULL; 1409 XtVaGetValues(fsb->grid, XmNrowPtr, row, XmNcolumnPtr, col, XmNrowUserData, &elm, NULL); 1410 if(elm) { 1411 if(!strcmp(item, FileName(elm->path))) { 1412 XmLGridSelectRow(fsb->grid, i, False); 1413 XmLGridFocusAndShowRow(fsb->grid, i+1); 1414 break; 1415 } 1416 } 1417 } 1418 } 1419 1420 static void FileListSelect(FileDialogData *fsb, const char *item) { 1421 int numItems = 0; 1422 XmStringTable items = NULL; 1423 XtVaGetValues(fsb->filelist, XmNitemCount, &numItems, XmNitems, &items, NULL); 1424 1425 for(int i=0;i<numItems;i++) { 1426 char *str = NULL; 1427 XmStringGetLtoR(items[i], XmFONTLIST_DEFAULT_TAG, &str); 1428 if(!strcmp(str, item)) { 1429 XmListSelectPos(fsb->filelist, i+1, False); 1430 break; 1431 } 1432 XtFree(str); 1433 } 1434 } 1435 1436 static void FileIconViewSelect(FileDialogData *fsb, const char *item) { 1437 // TODO 1438 } 1439 1440 static void FileSelect(FileDialogData *fsb, const char *item) { 1441 FileListSelect(fsb, item); 1442 FileListDetailSelect(fsb, item), 1443 FileIconViewSelect(fsb, item); 1444 } 1445 1446 1447 1448 #define FILE_ARRAY_SIZE 1024 1449 1450 void file_array_add(FileElm **files, int *alloc, int *count, FileElm elm) { 1451 int c = *count; 1452 int a = *alloc; 1453 if(c >= a) { 1454 a *= 2; 1455 FileElm *newarray = realloc(*files, sizeof(FileElm) * a); 1456 1457 *files = newarray; 1458 *alloc = a; 1459 } 1460 1461 (*files)[c] = elm; 1462 c++; 1463 *count = c; 1464 } 1465 1466 static void filedialog_update_dir(FileDialogData *data, char *path) 1467 { 1468 ViewUpdateFunc update_view = NULL; 1469 switch(data->selectedview) { 1470 case 1: { 1471 cleanupLists(data); 1472 update_view = filedialog_update_lists; 1473 break; 1474 } 1475 case 2: { 1476 cleanupGrid(data); 1477 update_view = filedialog_update_grid; 1478 break; 1479 } 1480 } 1481 1482 char *openFile = NULL; 1483 1484 /* read dir and insert items */ 1485 if(path) { 1486 struct stat s; 1487 int r = stat(path, &s); 1488 if((!r == !S_ISDIR(s.st_mode)) || (r && errno == ENOENT && data->type == FILEDIALOG_SAVE)) { 1489 // open file 1490 if(data->selectedPath) { 1491 NEditFree(data->selectedPath); 1492 } 1493 data->selectedPath = NEditStrdup(path); 1494 1495 openFile = FileName(path); 1496 path = ParentPath(path); 1497 1498 PathBarSetPath(data->pathBar, path); 1499 } 1500 1501 FileElm *dirs = calloc(sizeof(FileElm), FILE_ARRAY_SIZE); 1502 FileElm *files = calloc(sizeof(FileElm), FILE_ARRAY_SIZE); 1503 int dirs_alloc = FILE_ARRAY_SIZE; 1504 int files_alloc = FILE_ARRAY_SIZE; 1505 1506 filedialog_cleanup_filedata(data); 1507 1508 int dircount = 0; 1509 int filecount = 0; 1510 size_t maxNameLen = 0; 1511 DIR *dir = opendir(path); 1512 if(!dir) { 1513 DialogF( 1514 DF_ERR, 1515 data->shell, 1516 1, 1517 "Error", 1518 "Directory %s cannot be opened: %s", 1519 "OK", 1520 path, 1521 strerror(errno)); 1522 return; 1523 } 1524 1525 /* dir reading complete - set the path textfield */ 1526 char *oldPath = data->currentPath; 1527 data->currentPath = NEditStrdup(path); 1528 if(oldPath) { 1529 NEditFree(oldPath); 1530 } 1531 if(openFile) { 1532 NEditFree(path); 1533 } 1534 path = data->currentPath; 1535 1536 struct dirent *ent; 1537 while((ent = readdir(dir)) != NULL) { 1538 if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { 1539 continue; 1540 } 1541 1542 char *entpath = ConcatPath(path, ent->d_name); 1543 1544 struct stat s; 1545 if(stat(entpath, &s)) { 1546 NEditFree(entpath); 1547 continue; 1548 } 1549 1550 FileElm new_entry; 1551 new_entry.path = entpath; 1552 new_entry.isDirectory = S_ISDIR(s.st_mode); 1553 new_entry.size = (uint64_t)s.st_size; 1554 new_entry.lastModified = s.st_mtime; 1555 1556 size_t nameLen = strlen(ent->d_name); 1557 if(nameLen > maxNameLen) { 1558 maxNameLen = nameLen; 1559 } 1560 1561 if(new_entry.isDirectory) { 1562 file_array_add(&dirs, &dirs_alloc, &dircount, new_entry); 1563 } else { 1564 file_array_add(&files, &files_alloc, &filecount, new_entry); 1565 } 1566 } 1567 closedir(dir); 1568 1569 data->dirs = dirs; 1570 data->files = files; 1571 data->dircount = dircount; 1572 data->filecount = filecount; 1573 data->maxnamelen = maxNameLen; 1574 1575 // sort file arrays 1576 qsort(dirs, dircount, sizeof(FileElm), filecmp); 1577 qsort(files, filecount, sizeof(FileElm), filecmp); 1578 } 1579 1580 update_view(data, data->dirs, data->files, 1581 data->dircount, data->filecount, data->maxnamelen); 1582 1583 if(openFile) { 1584 FileSelect(data, openFile); 1585 data->status = FILEDIALOG_OK; 1586 if(data->name) { 1587 XNETextSetString(data->name, openFile); 1588 XmProcessTraversal(data->name, XmTRAVERSE_CURRENT); 1589 } else { 1590 select_view(data); 1591 } 1592 } 1593 } 1594 1595 static void filedialog_goup(Widget w, FileDialogData *data, XtPointer d) 1596 { 1597 char *newPath = ParentPath(data->currentPath); 1598 filedialog_update_dir(data, newPath); 1599 PathBarSetPath(data->pathBar, newPath); 1600 NEditFree(newPath); 1601 } 1602 1603 char* set_selected_path(FileDialogData *data, XmString item) 1604 { 1605 char *name = NULL; 1606 XmStringGetLtoR(item, XmFONTLIST_DEFAULT_TAG, &name); 1607 if(!name) { 1608 return NULL; 1609 } 1610 char *path = ConcatPath(data->currentPath, name); 1611 XtFree(name); 1612 1613 if(data->selectedPath) { 1614 NEditFree(data->selectedPath); 1615 } 1616 data->selectedPath = path; 1617 1618 return path; 1619 } 1620 1621 void set_path_from_row(FileDialogData *data, int row) { 1622 FileElm *elm = NULL; 1623 XmLGridRow rowPtr = XmLGridGetRow(data->grid, XmCONTENT, row); 1624 XtVaGetValues(data->grid, XmNrowPtr, rowPtr, XmNrowUserData, &elm, NULL); 1625 if(!elm) { 1626 fprintf(stderr, "error: no row data\n"); 1627 return; 1628 } 1629 1630 char *path = NEditStrdup(elm->path); 1631 filedialog_check_iofilters(data, path); 1632 1633 if(data->type == FILEDIALOG_SAVE) { 1634 XNETextSetString(data->name, FileName(path)); 1635 NEditFree(path); 1636 } else { 1637 if(data->selectedPath) { 1638 NEditFree(data->selectedPath); 1639 } 1640 data->selectedPath = path; 1641 data->selIsDir = False; 1642 } 1643 } 1644 1645 void grid_select(Widget w, FileDialogData *data, XmLGridCallbackStruct *cb) { 1646 set_path_from_row(data, cb->row); 1647 } 1648 1649 void grid_activate(Widget w, FileDialogData *data, XmLGridCallbackStruct *cb) { 1650 set_path_from_row(data, cb->row); 1651 filedialog_ok(w, data, NULL); 1652 } 1653 1654 void grid_key_pressed(Widget w, FileDialogData *data, XmLGridCallbackStruct *cb) { 1655 char chars[16]; 1656 KeySym keysym; 1657 int nchars; 1658 1659 nchars = XLookupString(&cb->event->xkey, chars, 15, &keysym, NULL); 1660 1661 if(nchars == 0) return; 1662 1663 // if data->showHidden is 0, data->files contains more items than the grid 1664 // this means SelectedRow might not be the correct index for data->files 1665 // we have to count files manually and increase 'row', if the file 1666 // is actually displayed in the grid 1667 int row = 0; 1668 int selectedRow = XmLGridGetSelectedRow(w); 1669 1670 int match = -1; 1671 1672 for(int i=0;i<data->filecount;i++) { 1673 const char *name = FileName(data->files[i].path); 1674 if(!data->showHidden && name[0] == '.') continue; 1675 1676 size_t namelen = strlen(name); 1677 1678 size_t cmplen = namelen < nchars ? namelen : nchars; 1679 if(!memcmp(name, chars, cmplen)) { 1680 if(row <= selectedRow) { 1681 if(match == -1) { 1682 match = row; 1683 } 1684 } else { 1685 match = row; 1686 break; 1687 } 1688 } 1689 1690 row++; 1691 } 1692 1693 if(match > -1) { 1694 XmLGridSelectRow(w, match, True); 1695 XmLGridFocusAndShowRow(w, match+1); 1696 } else { 1697 XBell(XtDisplay(w), 0); 1698 } 1699 } 1700 1701 void grid_header_clicked(Widget w, FileDialogData *data, XmLGridCallbackStruct *cb) { 1702 int new_cmp_field = 0; 1703 switch(cb->column) { 1704 case 0: { 1705 new_cmp_field = 0; 1706 break; 1707 } 1708 case 1: { 1709 new_cmp_field = 1; 1710 break; 1711 } 1712 case 2: { 1713 new_cmp_field = 2; 1714 break; 1715 } 1716 } 1717 1718 if(new_cmp_field == file_cmp_field) { 1719 file_cmp_order = -file_cmp_order; // revert sort order 1720 } else { 1721 file_cmp_field = new_cmp_field; // change file cmp order to new field 1722 file_cmp_order = 1; 1723 } 1724 1725 int sort_type = file_cmp_order == 1 ? XmSORT_ASCENDING : XmSORT_DESCENDING; 1726 XmLGridSetSort(data->grid, file_cmp_field, sort_type); 1727 1728 qsort(data->files, data->filecount, sizeof(FileElm), filecmp); 1729 1730 // refresh widget 1731 filedialog_update_dir(data, NULL); 1732 } 1733 1734 void dirlist_activate(Widget w, FileDialogData *data, XmListCallbackStruct *cb) 1735 { 1736 char *path = set_selected_path(data, cb->item); 1737 if(path) { 1738 filedialog_update_dir(data, path); 1739 PathBarSetPath(data->pathBar, path); 1740 data->selIsDir = TRUE; 1741 } 1742 } 1743 1744 void dirlist_select(Widget w, FileDialogData *data, XmListCallbackStruct *cb) 1745 { 1746 char *path = set_selected_path(data, cb->item); 1747 if(path) { 1748 data->selIsDir = TRUE; 1749 } 1750 } 1751 1752 void filelist_activate(Widget w, FileDialogData *data, XmListCallbackStruct *cb) 1753 { 1754 char *path = set_selected_path(data, cb->item); 1755 if(path) { 1756 data->selIsDir = False; 1757 filedialog_ok(w, data, NULL); 1758 } 1759 } 1760 1761 void filelist_select(Widget w, FileDialogData *data, XmListCallbackStruct *cb) 1762 { 1763 if(data->type == FILEDIALOG_SAVE) { 1764 char *name = NULL; 1765 XmStringGetLtoR(cb->item, XmFONTLIST_DEFAULT_TAG, &name); 1766 XNETextSetString(data->name, name); 1767 char *path = name ? ConcatPath(data->currentPath, name) : NULL; 1768 XtFree(name); 1769 filedialog_check_iofilters(data, path); 1770 XtFree(path); 1771 } else { 1772 char *path = set_selected_path(data, cb->item); 1773 filedialog_check_iofilters(data, path); 1774 if(path) { 1775 data->selIsDir = False; 1776 } 1777 } 1778 } 1779 1780 static void filedialog_setshowhidden( 1781 Widget w, 1782 FileDialogData *data, 1783 XmToggleButtonCallbackStruct *tb) 1784 { 1785 data->showHidden = tb->set; 1786 filedialog_update_dir(data, NULL); 1787 } 1788 1789 static void filedilalog_ok_end(FileDialogData *data) 1790 { 1791 struct stat s; 1792 1793 if(data->type == FILEDIALOG_OPEN) { 1794 // check if the file can be opened 1795 int fd = open(data->selectedPath, O_RDONLY); 1796 if(fd == -1) { 1797 FileOpenErrorDialog(data->shell, data->selectedPath); 1798 return; 1799 } else { 1800 close(fd); 1801 } 1802 1803 } else if(data->type == FILEDIALOG_SAVE && !stat(data->selectedPath, &s) && 1804 (!data->file_path || strcmp(data->file_path, data->selectedPath))) 1805 { 1806 if(OverrideFileDialog(data->shell, FileName(data->selectedPath)) != 1) { 1807 return; 1808 } 1809 } 1810 1811 data->status = FILEDIALOG_OK; 1812 data->end = True; 1813 } 1814 1815 static void filedialog_ok(Widget w, FileDialogData *data, XtPointer d) 1816 { 1817 XmPushButtonCallbackStruct *cb = d; 1818 if(cb && w != data->name) { 1819 if(cb->event->type == KeyPress && cb->event->xkey.keycode == 36) { 1820 return; 1821 } 1822 } 1823 1824 if(data->selectedPath) { 1825 if(!data->selIsDir) { 1826 filedilalog_ok_end(data); 1827 return; 1828 } 1829 } 1830 1831 if(data->type == FILEDIALOG_SAVE) { 1832 char *newName = XNETextGetString(data->name); 1833 if(newName) { 1834 if(strlen(newName) > 0) { 1835 data->selectedPath = newName[0] == '/' ? NEditStrdup(newName) : ConcatPath(data->currentPath, newName); 1836 data->selIsDir = 0; 1837 filedilalog_ok_end(data); 1838 } 1839 XtFree(newName); 1840 } 1841 } 1842 } 1843 1844 #define DETECT_ENCODING "detect" 1845 static char *default_encodings[] = { 1846 DETECT_ENCODING, 1847 "UTF-8", 1848 "UTF-16", 1849 "UTF-16BE", 1850 "UTF-16LE", 1851 "UTF-32", 1852 "UTF-32BE", 1853 "UTF-32LE", 1854 "ISO8859-1", 1855 "ISO8859-2", 1856 "ISO8859-3", 1857 "ISO8859-4", 1858 "ISO8859-5", 1859 "ISO8859-6", 1860 "ISO8859-7", 1861 "ISO8859-8", 1862 "ISO8859-9", 1863 "ISO8859-10", 1864 "ISO8859-13", 1865 "ISO8859-14", 1866 "ISO8859-15", 1867 "ISO8859-16", 1868 NULL 1869 }; 1870 1871 static void adjust_enc_settings(FileDialogData *data) { 1872 /* this should never happen, but make sure it will not do anything */ 1873 if(data->type != FILEDIALOG_SAVE) return; 1874 1875 int encPos; 1876 XtVaGetValues(data->encoding, XmNselectedPosition, &encPos, NULL); 1877 1878 /* 1879 * the save file dialog doesn't has the "detect" item 1880 * the default_encodings index is encPos + 1 1881 */ 1882 if(encPos > 6) { 1883 /* no unicode no bom */ 1884 XtSetSensitive(data->bom, False); 1885 if(GetAutoEnableXattr()) { 1886 XtVaSetValues(data->xattr, XmNset, 1, NULL); 1887 } 1888 } else { 1889 XtSetSensitive(data->bom, True); 1890 if(encPos > 0) { 1891 /* enable bom for all non-UTF-8 unicode encodings */ 1892 XtVaSetValues(data->bom, XmNset, 1, NULL); 1893 } 1894 } 1895 } 1896 1897 static void filedialog_select_encoding( 1898 Widget w, 1899 FileDialogData *data, 1900 XmComboBoxCallbackStruct *cb) 1901 { 1902 if(cb->reason == XmCR_SELECT) { 1903 adjust_enc_settings(data); 1904 } 1905 } 1906 1907 static int str_has_suffix(const char *str, const char *suffix) { 1908 size_t str_len = str ? strlen(str) : 0; 1909 size_t suffix_len = suffix ? strlen(suffix) : 0; 1910 if(str_len >= suffix_len) { 1911 const char *str_end = str + str_len - suffix_len; 1912 int result = memcmp(str_end, suffix, suffix_len) == 0; 1913 return result; 1914 } 1915 return 0; 1916 } 1917 1918 static void filedialog_select_iofilter( 1919 Widget w, 1920 FileDialogData *data, 1921 XmComboBoxCallbackStruct *cb) 1922 { 1923 if(cb->reason == XmCR_SELECT) { 1924 if(data->type == FILEDIALOG_SAVE) { 1925 // remove previous extension 1926 if(data->selected_filter) { 1927 // compare current file name extension with selected_filter extension 1928 char *name = XNETextGetString(data->name); 1929 size_t name_len = strlen(name); 1930 if(str_has_suffix(name, data->selected_filter->ext)) { 1931 name[name_len - strlen(data->selected_filter->ext)] = 0; // remove ext 1932 XNETextSetString(data->name, name); 1933 } 1934 XtFree(name); 1935 } 1936 } 1937 1938 // get the current filter from the combobox 1939 int selectedFilterIndex; 1940 XtVaGetValues(data->iofilter, XmNselectedPosition, &selectedFilterIndex, NULL); 1941 // index 0 is always '-' no filter 1942 if(selectedFilterIndex == 0) { 1943 data->selected_filter = NULL; 1944 } else { 1945 // combobox indices are always +1 compared to data->filters indices 1946 data->selected_filter = data->filters[selectedFilterIndex-1]; 1947 1948 // set extension if the name doesn't already contain the extension 1949 if(data->name && data->selected_filter->ext) { 1950 char *name = XNETextGetString(data->name); 1951 if(!str_has_suffix(name, data->selected_filter->ext)) { 1952 size_t name_len = strlen(name); 1953 size_t ext_len = strlen(data->selected_filter->ext); 1954 size_t newname_len = name_len + ext_len; 1955 char *newname = NEditMalloc(newname_len + 1); 1956 memcpy(newname, name, name_len); 1957 memcpy(newname+name_len, data->selected_filter->ext, ext_len); 1958 newname[newname_len] = '\0'; 1959 XNETextSetString(data->name, newname); 1960 NEditFree(newname); 1961 } 1962 XtFree(name); 1963 } 1964 } 1965 } 1966 } 1967 1968 static void filedialog_filter(Widget w, FileDialogData *data, XtPointer c) 1969 { 1970 filedialog_update_dir(data, NULL); 1971 } 1972 1973 static void unselect_view(FileDialogData *data) 1974 { 1975 switch(data->selectedview) { 1976 case 1: { 1977 XtUnmanageChild(data->listform); 1978 XtUnmanageChild(data->filelistcontainer); 1979 cleanupLists(data); 1980 break; 1981 } 1982 case 2: { 1983 XtUnmanageChild(data->listform); 1984 XtUnmanageChild(data->gridcontainer); 1985 cleanupGrid(data); 1986 1987 // reset sort options and resort files 1988 file_cmp_field = 0; 1989 file_cmp_order = 1; 1990 qsort(data->files, data->filecount, sizeof(FileElm), filecmp); 1991 1992 break; 1993 } 1994 } 1995 } 1996 1997 static void select_listview(Widget w, FileDialogData *data, XtPointer u) 1998 { 1999 unselect_view(data); 2000 data->selectedview = 1; 2001 XtManageChild(data->listform); 2002 XtManageChild(data->filelistcontainer); 2003 filedialog_update_dir(data, NULL); 2004 data->pathBar->focus_widget = data->filelist; 2005 XmProcessTraversal(data->filelist, XmTRAVERSE_CURRENT); 2006 } 2007 2008 static void select_detailview(Widget w, FileDialogData *data, XtPointer u) 2009 { 2010 //XmLGridSetSort(data->grid, 0, XmSORT_ASCENDING); 2011 2012 unselect_view(data); 2013 data->selectedview = 2; 2014 XtManageChild(data->listform); 2015 XtManageChild(data->gridcontainer); 2016 filedialog_update_dir(data, NULL); 2017 data->pathBar->focus_widget = data->grid; 2018 XmProcessTraversal(data->grid, XmTRAVERSE_CURRENT); 2019 } 2020 2021 static void select_view(FileDialogData *data) 2022 { 2023 Widget w; 2024 switch(data->selectedview) { 2025 default: return; 2026 case 0: w = data->grid; break; 2027 case 1: w = data->filelist; break; 2028 } 2029 XmProcessTraversal(w, XmTRAVERSE_CURRENT); 2030 } 2031 2032 static void new_folder(Widget w, FileDialogData *data, XtPointer u) 2033 { 2034 char fileName[DF_MAX_PROMPT_LENGTH]; 2035 fileName[0] = 0; 2036 2037 int response = DialogF( 2038 DF_PROMPT, 2039 data->shell, 2040 2, 2041 "Create new directory", "Directory name:", 2042 fileName, 2043 "OK", 2044 "Cancel"); 2045 2046 if(response == 2 || strlen(fileName) == 0) { 2047 return; 2048 } 2049 2050 char *newFolder = ConcatPath(data->currentPath ? data->currentPath : "", fileName); 2051 if(mkdir(newFolder, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { 2052 DialogF( 2053 DF_ERR, 2054 data->shell, 2055 1, 2056 "Error creating Directory", 2057 "Can''t create %s:\n%s", "OK", 2058 newFolder, 2059 strerror(errno)); 2060 } else { 2061 char *p = strdup(data->currentPath); 2062 filedialog_update_dir(data, p); 2063 free(p); 2064 } 2065 free(newFolder); 2066 } 2067 2068 static unsigned int keycodeL; 2069 2070 static void shortcutEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) 2071 { 2072 FileDialogData *fsb = data; 2073 if(event->xkey.keycode == keycodeL) { 2074 PathBarActivateTextfield(fsb->pathBar); 2075 *dispatch = False; 2076 } 2077 } 2078 2079 static void createFilterWidgets(FileDialogData *data, const char *current_filter, Widget parent, Arg *args, int n) { 2080 size_t nfilters; 2081 IOFilter **filter = GetFilterList(&nfilters); 2082 data->filters = filter; 2083 data->nfilters = nfilters; 2084 2085 XmStringTable filterStrTable = NEditCalloc(nfilters+1, sizeof(XmString)); 2086 filterStrTable[0] = XmStringCreateSimple("-"); 2087 for(int i=0;i<nfilters;i++) { 2088 filterStrTable[i+1] = XmStringCreateLocalized(filter[i]->name); 2089 } 2090 2091 XtSetArg(args[n], XmNcolumns, 11); n++; 2092 XtSetArg(args[n], XmNitemCount, nfilters+1); n++; 2093 XtSetArg(args[n], XmNitems, filterStrTable); n++; 2094 data->iofilter = XmCreateDropDownList(parent, "filtercombobox", args, n); 2095 XtManageChild(data->iofilter); 2096 for(int i=0;i<nfilters+1;i++) { 2097 XmStringFree(filterStrTable[i]); 2098 } 2099 NEditFree(filterStrTable); 2100 2101 XtAddCallback( 2102 data->iofilter, 2103 XmNselectionCallback, 2104 (XtCallbackProc)filedialog_select_iofilter, 2105 data); 2106 2107 if(current_filter) { 2108 XmString xCurrentFilter = XmStringCreateLocalized((char*)current_filter); 2109 XmComboBoxSelectItem(data->iofilter, xCurrentFilter); 2110 XmStringFree(xCurrentFilter); 2111 2112 XmComboBoxCallbackStruct cb; 2113 cb.reason = XmCR_SELECT; 2114 filedialog_select_iofilter(data->iofilter, data, &cb); 2115 } 2116 } 2117 2118 int FileDialog(Widget parent, char *promptString, FileSelection *file, int type, const char *defaultName) 2119 { 2120 Arg args[32]; 2121 int n = 0; 2122 XmString str; 2123 2124 int currentEncItem = 0; 2125 2126 if(LastView == -1) { 2127 LastView = GetFsbView(); 2128 if(LastView < 0 || LastView > 2) { 2129 LastView = 1; 2130 } 2131 } 2132 #ifndef FSB_ENABLE_DETAIL 2133 if(LastView == 2) { 2134 LastView = 1; 2135 } 2136 #endif 2137 Boolean showHiddenValue = GetFsbShowHidden(); 2138 2139 FileDialogData data; 2140 memset(&data, 0, sizeof(FileDialogData)); 2141 data.type = type; 2142 data.file_path = file->path; 2143 2144 file->addwrap = FALSE; 2145 file->setxattr = FALSE; 2146 2147 Widget dialog = CreateDialogShell(parent, promptString, args, 0); 2148 AddMotifCloseCallback(dialog, (XtCallbackProc)filedialog_cancel, &data); 2149 data.shell = dialog; 2150 2151 /* shortcut handler */ 2152 XtAddEventHandler(dialog, KeyPressMask , False, 2153 (XtEventHandler)shortcutEH, &data); 2154 2155 n = 0; 2156 XtSetArg(args[n], XmNautoUnmanage, False); n++; 2157 Widget form = XmCreateForm(dialog, "form", args, n); 2158 2159 /* upper part of the gui */ 2160 2161 n = 0; 2162 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2163 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 2164 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2165 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2166 XtSetArg(args[n], XmNresizable, True); n++; 2167 XtSetArg(args[n], XmNarrowDirection, XmARROW_UP); n++; 2168 Widget goUp = XmCreateArrowButton(form, "button", args, n); 2169 //XtManageChild(goUp); 2170 XtAddCallback(goUp, XmNactivateCallback, 2171 (XtCallbackProc)filedialog_goup, &data); 2172 2173 // View Option Menu 2174 n = 0; 2175 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2176 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 2177 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2178 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 2179 XtSetArg(args[n], XmNshadowThickness, 0); n++; 2180 Widget viewframe = XmCreateForm(form, "vframe", args, n); 2181 XtManageChild(viewframe); 2182 2183 XmString v1 = XmStringCreateLocalized("List"); 2184 XmString v2 = XmStringCreateLocalized("Detail"); 2185 2186 Widget menu = XmCreatePulldownMenu(viewframe, "menu", NULL, 0); 2187 2188 XtSetArg(args[0], XmNlabelString, v1); 2189 XtSetArg(args[1], XmNpositionIndex, LastView == 1 ? 0 : 1); 2190 Widget mitem1 = XmCreatePushButton(menu, "menuitem", args, 2); 2191 XtSetArg(args[0], XmNlabelString, v2); 2192 XtSetArg(args[1], XmNpositionIndex, LastView == 2 ? 0 : 2); 2193 Widget mitem2 = XmCreatePushButton(menu, "menuitem", args, 2); 2194 XtManageChild(mitem1); 2195 #ifdef FSB_ENABLE_DETAIL 2196 XtManageChild(mitem2); 2197 #endif 2198 XmStringFree(v1); 2199 XmStringFree(v2);; 2200 XtAddCallback( 2201 mitem1, 2202 XmNactivateCallback, 2203 (XtCallbackProc)select_listview, 2204 &data); 2205 XtAddCallback( 2206 mitem2, 2207 XmNactivateCallback, 2208 (XtCallbackProc)select_detailview, 2209 &data); 2210 2211 n = 0; 2212 XtSetArg(args[n], XmNsubMenuId, menu); n++; 2213 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2214 XtSetArg(args[n], XmNmarginHeight, 0); n++; 2215 XtSetArg(args[n], XmNmarginWidth, 0); n++; 2216 Widget view = XmCreateOptionMenu(viewframe, "option_menu", args, n); 2217 XtManageChild(view); 2218 2219 n = 0; 2220 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2221 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 2222 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2223 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2224 XtSetArg(args[n], XmNrightWidget, view); n++; 2225 XtSetArg(args[n], XmNmarginHeight, 0); n++; 2226 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++; 2227 Widget newFolder = XmCreatePushButton(viewframe, "newFolder", args, n); 2228 XtManageChild(newFolder); 2229 XtAddCallback( 2230 newFolder, 2231 XmNactivateCallback, 2232 (XtCallbackProc)new_folder, 2233 &data); 2234 2235 Dimension xh; 2236 Pixel buttonFg; 2237 Pixel buttonBg; 2238 XtVaGetValues(newFolder, XmNheight, &xh, XmNforeground, &buttonFg, XmNbackground, &buttonBg, NULL); 2239 2240 // get rgb value of buttonBg 2241 memset(&bgColor, 0, sizeof(XColor)); 2242 bgColor.pixel = buttonBg; 2243 XQueryColor(XtDisplay(newFolder), newFolder->core.colormap, &bgColor); 2244 2245 // init pixmaps after we got the background color 2246 if(!pixmaps_initialized) { 2247 initPixmaps(XtDisplay(parent), XtWindow(parent), newFolder->core.screen, newFolder->core.depth); 2248 } 2249 2250 if(pixmaps_error) { 2251 XtVaSetValues(newFolder, XmNlabelType, XmSTRING, NULL); 2252 } else if(xh > 32+BUTTON_EXTRA_SPACE) { 2253 XtVaSetValues(newFolder, XmNlabelPixmap, newFolderIcon32, NULL); 2254 } else if(xh > 24+BUTTON_EXTRA_SPACE) { 2255 XtVaSetValues(newFolder, XmNlabelPixmap, newFolderIcon24, NULL); 2256 } else { 2257 XtVaSetValues(newFolder, XmNlabelPixmap, newFolderIcon16, NULL); 2258 } 2259 2260 // pathbar 2261 n = 0; 2262 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2263 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 2264 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; 2265 XtSetArg(args[n], XmNleftWidget, goUp); n++; 2266 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2267 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 2268 XtSetArg(args[n], XmNrightWidget, viewframe); n++; 2269 XtSetArg(args[n], XmNrightOffset, WIDGET_SPACING); n++; 2270 XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); n++; 2271 Widget pathBarFrame = XmCreateFrame(form, "pathbar_frame", args, n); 2272 XtManageChild(pathBarFrame); 2273 data.pathBar = CreatePathBar(pathBarFrame, args, 0); 2274 data.pathBar->updateDir = (updatedir_callback)filedialog_update_dir; 2275 data.pathBar->updateDirData = &data; 2276 XtManageChild(data.pathBar->widget); 2277 2278 n = 0; 2279 str = XmStringCreateLocalized("Filter"); 2280 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2281 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2282 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 2283 XtSetArg(args[n], XmNtopWidget, pathBarFrame); n++; 2284 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 2285 XtSetArg(args[n], XmNlabelString, str); n++; 2286 Widget filterLabel = XmCreateLabel(form, "label", args, n); 2287 XtManageChild(filterLabel); 2288 XmStringFree(str); 2289 2290 n = 0; 2291 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2292 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2293 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 2294 XtSetArg(args[n], XmNtopWidget, filterLabel); n++; 2295 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 2296 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2297 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 2298 Widget filterform = XmCreateForm(form, "filterform", args, n); 2299 XtManageChild(filterform); 2300 2301 n = 0; 2302 str = XmStringCreateSimple("Show hidden files"); 2303 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2304 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2305 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2306 XtSetArg(args[n], XmNlabelString, str); n++; 2307 XtSetArg(args[n], XmNset, showHiddenValue); n++; 2308 Widget showHidden = XmCreateToggleButton(filterform, "showHidden", args, n); 2309 XtManageChild(showHidden); 2310 XmStringFree(str); 2311 XtAddCallback(showHidden, XmNvalueChangedCallback, 2312 (XtCallbackProc)filedialog_setshowhidden, &data); 2313 data.showHidden = showHiddenValue; 2314 2315 n = 0; 2316 str = XmStringCreateLocalized("Filter"); 2317 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2318 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2319 XtSetArg(args[n], XmNlabelString, str); n++; 2320 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 2321 XtSetArg(args[n], XmNrightWidget, showHidden); n++; 2322 Widget filterButton = XmCreatePushButton(filterform, "filedialog_filter", args, n); 2323 XtManageChild(filterButton); 2324 XmStringFree(str); 2325 XtAddCallback(filterButton, XmNactivateCallback, 2326 (XtCallbackProc)filedialog_filter, &data); 2327 2328 n = 0; 2329 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2330 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2331 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2332 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 2333 XtSetArg(args[n], XmNrightWidget, filterButton); n++; 2334 XtSetArg(args[n], XmNrightOffset, WIDGET_SPACING); n++; 2335 data.filter = XNECreateText(filterform, "filedialog_filter_textfield", args, n); 2336 XtManageChild(data.filter); 2337 XtAddCallback(data.filter, XmNactivateCallback, 2338 (XtCallbackProc)filedialog_filter, &data); 2339 if(LastFilter) { 2340 XNETextSetString(data.filter, LastFilter); 2341 XtFree(LastFilter); 2342 LastFilter = NULL; 2343 } else { 2344 XNETextSetString(data.filter, "*"); 2345 } 2346 2347 /* lower part */ 2348 n = 0; 2349 str = XmStringCreateLocalized(type == FILEDIALOG_OPEN ? "Open" : "Save"); 2350 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2351 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 2352 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2353 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2354 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING * 2); n++; 2355 XtSetArg(args[n], XmNlabelString, str); n++; 2356 data.okBtn = XmCreatePushButton(form, "filedialog_open", args, n); 2357 XtManageChild(data.okBtn); 2358 XmStringFree(str); 2359 XtAddCallback(data.okBtn, XmNactivateCallback, 2360 (XtCallbackProc)filedialog_ok, &data); 2361 2362 n = 0; 2363 str = XmStringCreateLocalized("Cancel"); 2364 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2365 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 2366 XtSetArg(args[n], XmNlabelString, str); n++; 2367 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2368 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 2369 Widget cancelBtn = XmCreatePushButton(form, "filedialog_cancel", args, n); 2370 XtManageChild(cancelBtn); 2371 XmStringFree(str); 2372 XtAddCallback(cancelBtn, XmNactivateCallback, 2373 (XtCallbackProc)filedialog_cancel, &data); 2374 2375 XtVaSetValues(form, XmNdefaultButton, data.okBtn, XmNcancelButton, cancelBtn, NULL); 2376 2377 n = 0; 2378 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2379 XtSetArg(args[n], XmNbottomWidget, data.okBtn); n++; 2380 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2381 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2382 XtSetArg(args[n], XmNleftOffset, 1); n++; 2383 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2384 XtSetArg(args[n], XmNrightOffset, 1); n++; 2385 Widget separator = XmCreateSeparator(form, "ofd_separator", args, n); 2386 XtManageChild(separator); 2387 2388 Widget bottomWidget = separator; 2389 2390 if(type == FILEDIALOG_SAVE) { 2391 if(file->extraoptions) { 2392 n = 0; 2393 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2394 XtSetArg(args[n], XmNbottomWidget, separator); n++; 2395 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2396 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2397 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 2398 createFilterWidgets(&data, file->filter, form, args, n); 2399 2400 n = 0; 2401 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2402 XtSetArg(args[n], XmNbottomWidget, separator); n++; 2403 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2404 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2405 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2406 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 2407 XtSetArg(args[n], XmNrightWidget, data.iofilter); n++; 2408 XtSetArg(args[n], XmNrightOffset, WIDGET_SPACING); n++; 2409 //XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++; 2410 //XtSetArg(args[n], XmNtopWidget, data.iofilter); n++; 2411 data.name = XNECreateText(form, "textfield", args, n); 2412 XtManageChild(data.name); 2413 XtAddCallback(data.name, XmNactivateCallback, 2414 (XtCallbackProc)filedialog_ok, &data); 2415 if(defaultName) { 2416 XNETextSetString(data.name, (char*)defaultName); 2417 } 2418 2419 n = 0; 2420 str = XmStringCreateSimple("Filter"); 2421 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2422 XtSetArg(args[n], XmNbottomWidget, data.name); n++; 2423 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2424 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; 2425 XtSetArg(args[n], XmNleftWidget, data.name); n++; 2426 XtSetArg(args[n], XmNleftOffset, WIDGET_SPACING); n++; 2427 XtSetArg(args[n], XmNlabelString, str); n++; 2428 Widget filterLabel = XmCreateLabel(form, "label", args, n); 2429 XtManageChild(filterLabel); 2430 XmStringFree(str); 2431 2432 n = 0; 2433 str = XmStringCreateSimple("New File Name"); 2434 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2435 XtSetArg(args[n], XmNbottomWidget, data.name); n++; 2436 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2437 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2438 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2439 XtSetArg(args[n], XmNlabelString, str); n++; 2440 Widget nameLabel = XmCreateLabel(form, "label", args, n); 2441 XtManageChild(nameLabel); 2442 XmStringFree(str); 2443 2444 n = 0; 2445 str = XmStringCreateSimple("Add line breaks where wrapped"); 2446 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2447 XtSetArg(args[n], XmNbottomWidget, nameLabel); n++; 2448 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2449 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2450 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2451 XtSetArg(args[n], XmNmnemonic, 'A'); n++; 2452 XtSetArg(args[n], XmNlabelString, str); n++; 2453 data.wrap = XmCreateToggleButton(form, "addWrap", args, n); 2454 XtManageChild(data.wrap); 2455 XmStringFree(str); 2456 2457 Widget formatBtns = CreateFormatButtons( 2458 form, 2459 data.wrap, 2460 file->format, 2461 &data.unixFormat, 2462 &data.dosFormat, 2463 &data.macFormat); 2464 2465 bottomWidget = formatBtns; 2466 } else { 2467 n = 0; 2468 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2469 XtSetArg(args[n], XmNbottomWidget, separator); n++; 2470 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2471 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2472 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2473 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2474 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 2475 //XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++; 2476 //XtSetArg(args[n], XmNtopWidget, data.iofilter); n++; 2477 data.name = XNECreateText(form, "textfield", args, n); 2478 XtManageChild(data.name); 2479 XtAddCallback(data.name, XmNactivateCallback, 2480 (XtCallbackProc)filedialog_ok, &data); 2481 if(defaultName) { 2482 XNETextSetString(data.name, (char*)defaultName); 2483 } 2484 2485 n = 0; 2486 str = XmStringCreateSimple("New File Name"); 2487 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2488 XtSetArg(args[n], XmNbottomWidget, data.name); n++; 2489 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2490 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2491 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2492 XtSetArg(args[n], XmNlabelString, str); n++; 2493 Widget nameLabel = XmCreateLabel(form, "label", args, n); 2494 XtManageChild(nameLabel); 2495 XmStringFree(str); 2496 2497 bottomWidget = nameLabel; 2498 } 2499 } 2500 2501 n = 0; 2502 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2503 XtSetArg(args[n], XmNbottomWidget, bottomWidget); n++; 2504 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2505 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2506 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2507 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++; 2508 Widget enc = XmCreateRowColumn(form, "enc", args, n); 2509 XtManageChild(enc); 2510 2511 if(file->extraoptions) { 2512 n = 0; 2513 str = XmStringCreateSimple("Encoding:"); 2514 XtSetArg(args[n], XmNlabelString, str); n++; 2515 Widget encLabel = XmCreateLabel(enc, "label", args, n); 2516 XtManageChild(encLabel); 2517 XmStringFree(str); 2518 2519 n = 0; 2520 int arraylen = 22; 2521 2522 // TODO: code dup of encoding list generation (window.c) 2523 2524 const char *encStr; 2525 XmStringTable encodings = NEditCalloc(arraylen, sizeof(XmString)); 2526 /* skip the "detect" item on type == save */ 2527 int skip = type == FILEDIALOG_OPEN ? 0 : 1; 2528 char *defEncoding = type == FILEDIALOG_OPEN ? NULL : file->encoding; 2529 int hasDef = 0; 2530 int i; 2531 for(i=skip;(encStr=default_encodings[i]);i++) { 2532 if(i >= arraylen) { 2533 arraylen *= 2; 2534 encodings = NEditRealloc(encodings, arraylen * sizeof(XmString)); 2535 } 2536 encodings[i] = XmStringCreateSimple((char*)encStr); 2537 if(defEncoding) { 2538 if(!strcasecmp(defEncoding, encStr)) { 2539 hasDef = 1; 2540 defEncoding = NULL; 2541 } 2542 } 2543 } 2544 if(skip == 1 && !hasDef && file->encoding) { 2545 /* Current encoding is not in the list of 2546 * default encodings 2547 * Add an extra item at pos 0 for the current encoding 2548 */ 2549 encodings[0] = XmStringCreateSimple(file->encoding); 2550 currentEncItem = 1; 2551 skip = 0; 2552 } 2553 XtSetArg(args[n], XmNcolumns, 11); n++; 2554 XtSetArg(args[n], XmNitemCount, i-skip); n++; 2555 XtSetArg(args[n], XmNitems, encodings+skip); n++; 2556 data.encoding = XmCreateDropDownList(enc, "combobox", args, n); 2557 XtManageChild(data.encoding); 2558 for(int j=0;j<i;j++) { 2559 XmStringFree(encodings[j]); 2560 } 2561 NEditFree(encodings); 2562 2563 if(file->encoding) { 2564 char *encStr = NEditStrdup(file->encoding); 2565 size_t encLen = strlen(encStr); 2566 for(int i=0;i<encLen;i++) { 2567 encStr[i] = toupper(encStr[i]); 2568 } 2569 str = XmStringCreateSimple(encStr); 2570 XmComboBoxSelectItem(data.encoding, str); 2571 XmStringFree(str); 2572 NEditFree(encStr); 2573 } 2574 2575 /* bom and xattr option */ 2576 if(type == FILEDIALOG_SAVE) { 2577 /* only the save file dialog needs an encoding select callback */ 2578 XtAddCallback( 2579 data.encoding, 2580 XmNselectionCallback, 2581 (XtCallbackProc)filedialog_select_encoding, 2582 &data); 2583 2584 n = 0; 2585 str = XmStringCreateSimple("Write BOM"); 2586 XtSetArg(args[n], XmNlabelString, str); n++; 2587 XtSetArg(args[n], XmNset, file->writebom); n++; 2588 data.bom = XmCreateToggleButton(enc, "togglebutton", args, n); 2589 XtManageChild(data.bom); 2590 XmStringFree(str); 2591 2592 n = 0; 2593 str = XmStringCreateSimple("Store encoding in extended attribute"); 2594 XtSetArg(args[n], XmNlabelString, str); n++; 2595 data.xattr = XmCreateToggleButton(enc, "togglebutton", args, n); 2596 XtManageChild(data.xattr); 2597 XmStringFree(str); 2598 } else { 2599 // FILEDIALOG_OPEN 2600 n = 0; 2601 XmString str = XmStringCreateSimple("Filter"); 2602 XtSetArg(args[n], XmNlabelString, str); n++; 2603 Widget filterLabel = XmCreateLabel(enc, "filter_label", args, n); 2604 XtManageChild(filterLabel); 2605 XmStringFree(str); 2606 2607 createFilterWidgets(&data, file->filter, enc, args, 0); 2608 } 2609 2610 } 2611 2612 /* middle */ 2613 data.selectedview = LastView; 2614 2615 // form for dir/file lists 2616 n = 0; 2617 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2618 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 2619 XtSetArg(args[n], XmNtopWidget, filterform); n++; 2620 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 2621 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2622 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2623 XtSetArg(args[n], XmNbottomWidget, enc); n++; 2624 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 2625 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 2626 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 2627 XtSetArg(args[n], XmNwidth, 580); n++; 2628 XtSetArg(args[n], XmNheight, 400); n++; 2629 data.listform = XmCreateForm(form, "fds_listform", args, n); 2630 2631 // dir/file lists 2632 n = 0; 2633 str = XmStringCreateLocalized("Directories"); 2634 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2635 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2636 XtSetArg(args[n], XmNlabelString, str); n++; 2637 Widget lsDirLabel = XmCreateLabel(data.listform, "label", args, n); 2638 XtManageChild(lsDirLabel); 2639 XmStringFree(str); 2640 2641 n = 0; 2642 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2643 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 2644 XtSetArg(args[n], XmNtopWidget, lsDirLabel); n++; 2645 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 2646 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2647 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 2648 XtSetArg(args[n], XmNrightPosition, 35); n++; 2649 data.dirlist = XmCreateScrolledList(data.listform, "dirlist", args, n); 2650 Dimension w, h; 2651 XtMakeResizeRequest(data.dirlist, 150, 200, &w, &h); 2652 XtManageChild(data.dirlist); 2653 XtAddCallback( 2654 data.dirlist, 2655 XmNdefaultActionCallback, 2656 (XtCallbackProc)dirlist_activate, 2657 &data); 2658 XtAddCallback( 2659 data.dirlist, 2660 XmNbrowseSelectionCallback, 2661 (XtCallbackProc)dirlist_select, 2662 &data); 2663 2664 n = 0; 2665 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2666 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 2667 XtSetArg(args[n], XmNtopWidget, lsDirLabel); n++; 2668 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 2669 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2670 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; 2671 XtSetArg(args[n], XmNleftWidget, data.dirlist); n++; 2672 XtSetArg(args[n], XmNleftOffset, WIDGET_SPACING); n++; 2673 XtSetArg(args[n], XmNshadowThickness, 0); n++; 2674 data.filelistcontainer = XmCreateFrame(data.listform, "filelistframe", args, n); 2675 //XtManageChild(data.filelistcontainer); 2676 2677 data.filelist = XmCreateScrolledList(data.filelistcontainer, "filelist", NULL, 0); 2678 XtManageChild(data.filelist); 2679 XtAddCallback( 2680 data.filelist, 2681 XmNdefaultActionCallback, 2682 (XtCallbackProc)filelist_activate, 2683 &data); 2684 XtAddCallback( 2685 data.filelist, 2686 XmNbrowseSelectionCallback, 2687 (XtCallbackProc)filelist_select, 2688 &data); 2689 2690 // Detail FileList 2691 // the detail view shares widgets with the list view 2692 // switching between list and detail view only changes 2693 // filelistcontainer <-> gridcontainer 2694 n = 0; 2695 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2696 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 2697 XtSetArg(args[n], XmNtopWidget, lsDirLabel); n++; 2698 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 2699 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2700 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; 2701 XtSetArg(args[n], XmNleftWidget, data.dirlist); n++; 2702 XtSetArg(args[n], XmNleftOffset, WIDGET_SPACING); n++; 2703 XtSetArg(args[n], XmNshadowThickness, 0); n++; 2704 data.gridcontainer = XmCreateFrame(data.listform, "gridcontainer", args, n); 2705 //XtManageChild(data.gridcontainer); 2706 2707 n = 0; 2708 XtSetArg(args[n], XmNcolumns, 3); n++; 2709 XtSetArg(args[n], XmNheadingColumns, 0); n++; 2710 XtSetArg(args[n], XmNheadingRows, 1); n++; 2711 XtSetArg(args[n], XmNallowColumnResize, 1); n++; 2712 XtSetArg(args[n], XmNsimpleHeadings, "Name|Size|Last Modified"); n++; 2713 XtSetArg(args[n], XmNhorizontalSizePolicy, XmCONSTANT); n++; 2714 2715 data.grid = XmLCreateGrid(data.gridcontainer, "grid", args, n); 2716 XmLGridSetIgnoreModifyVerify(data.grid, True); 2717 int sort_type = file_cmp_order == 1 ? XmSORT_ASCENDING : XmSORT_DESCENDING; 2718 XmLGridSetSort(data.grid, file_cmp_field, sort_type); 2719 XtManageChild(data.grid); 2720 2721 // get the XmList background color 2722 const char *cellBg = "white"; 2723 XrmValue value; 2724 char *resourceType = NULL; 2725 2726 // Get the resource value from the resource database 2727 if(XrmGetResource(XtDatabase(XtDisplay(data.grid)), "XmList.background", NULL, &resourceType, &value)) { 2728 if(!strcmp(resourceType, "String")) { 2729 cellBg = value.addr; 2730 } 2731 } 2732 2733 XtVaSetValues( 2734 data.grid, 2735 XmNcellDefaults, True, 2736 XtVaTypedArg, XmNblankBackground, XmRString, cellBg, strlen(cellBg), 2737 XtVaTypedArg, XmNcellBackground, XmRString, cellBg, strlen(cellBg), 2738 NULL); 2739 2740 //XmLGridSetStrings(data.grid, "Name|Size|Last Modified"); 2741 XtAddCallback(data.grid, XmNselectCallback, (XtCallbackProc)grid_select, &data); 2742 XtAddCallback(data.grid, XmNactivateCallback, (XtCallbackProc)grid_activate, &data); 2743 XtAddCallback(data.grid, XmNheaderClickCallback, (XtCallbackProc)grid_header_clicked, &data); 2744 XtAddCallback(data.grid, XmNgridKeyPressedCallback, (XtCallbackProc)grid_key_pressed, &data); 2745 2746 2747 n = 0; 2748 str = XmStringCreateLocalized("Files"); 2749 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; 2750 XtSetArg(args[n], XmNleftWidget, data.dirlist); n++; 2751 XtSetArg(args[n], XmNleftOffset, WIDGET_SPACING); n++; 2752 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++; 2753 XtSetArg(args[n], XmNbottomWidget, lsDirLabel); n++; 2754 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2755 XtSetArg(args[n], XmNlabelString, str); n++; 2756 Widget lsFileLabel = XmCreateLabel(data.listform, "label", args, n); 2757 XtManageChild(lsFileLabel); 2758 XmStringFree(str); 2759 2760 Widget focus = NULL; 2761 switch(data.selectedview) { 2762 case 1: XtManageChild(data.listform); XtManageChild(data.filelistcontainer); focus = data.filelist; break; 2763 case 2: XtManageChild(data.listform); XtManageChild(data.gridcontainer); focus = data.grid; break; 2764 } 2765 if(data.type == FILEDIALOG_SAVE) { 2766 focus = data.name; 2767 } 2768 data.pathBar->focus_widget = focus; 2769 2770 if(file->path) { 2771 char *defDir = ParentPath(file->path); 2772 filedialog_update_dir(&data, defDir); 2773 PathBarSetPath(data.pathBar, defDir); 2774 NEditFree(defDir); 2775 2776 XNETextSetString(data.name, FileName(file->path)); 2777 } else { 2778 char *defDirStr = GetDefaultDirectoryStr(); 2779 char *defDir = defDirStr ? defDirStr : getenv("HOME"); 2780 2781 filedialog_update_dir(&data, defDir); 2782 PathBarSetPath(data.pathBar, defDir); 2783 } 2784 2785 //init_container_size(&data); 2786 2787 /* event loop */ 2788 keycodeL = XKeysymToKeycode(XtDisplay(dialog), XStringToKeysym("L")); 2789 XtGrabKey( 2790 dialog, 2791 keycodeL, 2792 ControlMask, 2793 True, 2794 GrabModeAsync, 2795 GrabModeAsync); 2796 ManageDialogCenteredOnPointer(form); 2797 2798 XmProcessTraversal(focus, XmTRAVERSE_CURRENT); 2799 2800 XtAppContext app = XtWidgetToApplicationContext(dialog); 2801 while(!data.end && !XtAppGetExitFlag(app)) { 2802 XEvent event; 2803 XtAppNextEvent(app, &event); 2804 XtDispatchEvent(&event); 2805 } 2806 2807 LastView = data.selectedview; 2808 2809 if(data.selectedPath && !data.selIsDir && data.status == FILEDIALOG_OK) { 2810 file->path = data.selectedPath; 2811 data.selectedPath = NULL; 2812 2813 // remember filter string 2814 LastFilter = XNETextGetString(data.filter); 2815 if(LastFilter) { 2816 if(strlen(LastFilter) == 0) { 2817 XtFree(LastFilter); 2818 LastFilter = NULL; 2819 } 2820 } 2821 2822 file->filter = data.selected_filter ? NEditStrdup(data.selected_filter->name) : NULL; 2823 2824 if(file->extraoptions) { 2825 int encPos; 2826 XtVaGetValues(data.encoding, XmNselectedPosition, &encPos, NULL); 2827 if(type == FILEDIALOG_OPEN) { 2828 if(encPos > 0) { 2829 /* index 0 is the "detect" item which is not a valid 2830 encoding string that can be used later */ 2831 file->encoding = default_encodings[encPos]; 2832 } 2833 } else { 2834 if(currentEncItem) { 2835 /* first item is the current encoding that is not 2836 * in default_encodings */ 2837 if(encPos > 0) { 2838 file->encoding = default_encodings[encPos]; 2839 } 2840 } else { 2841 /* first item is "UTF-8" */ 2842 file->encoding = default_encodings[encPos+1]; 2843 } 2844 } 2845 } 2846 2847 if(type == FILEDIALOG_SAVE) { 2848 int bomVal = 0; 2849 int xattrVal = 0; 2850 int wrapVal = 0; 2851 if(data.bom) { 2852 XtVaGetValues(data.bom, XmNset, &bomVal, NULL); 2853 } 2854 if(data.xattr) { 2855 XtVaGetValues(data.xattr, XmNset, &xattrVal, NULL); 2856 } 2857 if(data.wrap) { 2858 XtVaGetValues(data.wrap, XmNset, &wrapVal, NULL); 2859 } 2860 2861 file->writebom = bomVal; 2862 file->setxattr = xattrVal; 2863 file->addwrap = wrapVal; 2864 2865 2866 if(data.unixFormat) { 2867 int formatVal = 0; 2868 XtVaGetValues(data.unixFormat, XmNset, &formatVal, NULL); 2869 if(formatVal) { 2870 file->format = UNIX_FILE_FORMAT; 2871 } else { 2872 XtVaGetValues(data.dosFormat, XmNset, &formatVal, NULL); 2873 if(formatVal) { 2874 file->format = DOS_FILE_FORMAT; 2875 } else { 2876 XtVaGetValues(data.macFormat, XmNset, &formatVal, NULL); 2877 if(formatVal) { 2878 file->format = MAC_FILE_FORMAT; 2879 } 2880 } 2881 } 2882 } else { 2883 file->format = 0; 2884 } 2885 } 2886 } else { 2887 data.status = FILEDIALOG_CANCEL; 2888 if(data.selectedPath) { 2889 NEditFree(data.selectedPath); 2890 } 2891 } 2892 2893 filedialog_cleanup_filedata(&data); 2894 PathBarDestroy(data.pathBar); 2895 if(data.currentPath) { 2896 NEditFree(data.currentPath); 2897 } 2898 XtUnmapWidget(dialog); 2899 XtDestroyWidget(dialog); 2900 return data.status; 2901 } 2902 2903 const char ** FileDialogDefaultEncodings(void) { 2904 return (const char **)default_encodings; 2905 } 2906 2907 char* FileDialogGetFilter(void) { 2908 return NEditStrdup(LastFilter ? LastFilter : "*"); 2909 } 2910 2911 void FileDialogSetFilter(const char *filterStr) { 2912 NEditFree(LastFilter); 2913 LastFilter = filterStr ? NEditStrdup(filterStr) : NULL; 2914 } 2915