1 /*
2 * file list management ("virtual photo album").
3 * (c) 2003 Gerd Hoffmann <gerd@kraxel.org>
4 *
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stddef.h>
10 #include <unistd.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <dirent.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17
18 #include <X11/Xlib.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/extensions/XShm.h>
21 #include <Xm/Xm.h>
22 #include <Xm/Form.h>
23 #include <Xm/Label.h>
24 #include <Xm/RowColumn.h>
25 #include <Xm/PushB.h>
26 #include <Xm/CascadeB.h>
27 #include <Xm/ScrolledW.h>
28 #include <Xm/SelectioB.h>
29 #include <Xm/Transfer.h>
30 #include <Xm/TransferP.h>
31 #include <Xm/Container.h>
32 #include <Xm/FileSB.h>
33 #include <Xm/Separator.h>
34
35 #include "RegEdit.h"
36 #include "ida.h"
37 #include "readers.h"
38 #include "viewer.h"
39 #include "browser.h"
40 #include "filter.h"
41 #include "x11.h"
42 #include "selections.h"
43 #include "filebutton.h"
44 #include "filelist.h"
45 #include "xdnd.h"
46 #include "idaconfig.h"
47
48 /*----------------------------------------------------------------------*/
49
50 struct list_handle;
51
52 struct list_handle {
53 char *filename;
54 struct list_head files;
55
56 Widget shell;
57 Widget scroll;
58 Widget container;
59 Widget status;
60 XmString details[DETAIL_COUNT+1];
61
62 Widget loadbox;
63 Widget savebox;
64
65 XtWorkProcId wproc;
66 };
67
68 /* ---------------------------------------------------------------------- */
69
filelist_add(struct list_handle * h,char * filename)70 static void filelist_add(struct list_handle *h, char *filename)
71 {
72 struct file_button *file;
73 char *tmp;
74
75 /* fixup filename */
76 if (0 == strncmp(filename,"file:",5))
77 filename += 5;
78 if (NULL != (tmp = strchr(filename,'\n')))
79 *tmp = 0;
80 if (NULL != (tmp = strchr(filename,'\r')))
81 *tmp = 0;
82 if (0 == strlen(filename))
83 return;
84
85 /* add file */
86 file = malloc(sizeof(*file));
87 memset(file,0,sizeof(*file));
88
89 tmp = strrchr(filename,'/');
90 if (!tmp)
91 goto oops;
92 file->basename = strdup(tmp+1);
93 file->filename = strdup(filename);
94
95 if (-1 == stat(file->filename,&file->st)) {
96 fprintf(stderr,"stat %s: %s\n",file->filename,strerror(errno));
97 goto oops;
98 }
99 if (!S_ISREG(file->st.st_mode)) {
100 fprintf(stderr,"%s: not a regular file\n",file->filename);
101 goto oops;
102 }
103
104 list_add_tail(&file->window,&h->files);
105 file_createwidgets(h->container, file);
106 XtManageChild(file->widget);
107 fileinfo_queue(file);
108 container_relayout(h->container);
109 return;
110
111 oops:
112 if (file->filename)
113 free(file->filename);
114 if (file->basename)
115 free(file->basename);
116 free(file);
117 }
118
filelist_file(struct list_handle * h,char * filename)119 static void filelist_file(struct list_handle *h, char *filename)
120 {
121 if (h->filename == filename)
122 return;
123 if (h->filename)
124 free(h->filename);
125 h->filename = strdup(filename);
126 XtVaSetValues(h->shell,XtNtitle,h->filename,NULL);
127 }
128
filelist_read(struct list_handle * h,char * filename)129 static void filelist_read(struct list_handle *h, char *filename)
130 {
131 FILE *fp;
132 char line[128];
133
134 fp = fopen(filename,"r");
135 if (NULL == fp) {
136 fprintf(stderr,"open %s: %s\n",filename,strerror(errno));
137 return;
138 }
139 while (NULL != fgets(line, sizeof(line), fp)) {
140 filelist_add(h, line);
141 }
142 fclose(fp);
143 filelist_file(h,filename);
144 container_relayout(h->container);
145 }
146
filelist_write(struct list_handle * h,char * filename)147 static void filelist_write(struct list_handle *h, char *filename)
148 {
149 struct file_button *file;
150 struct list_head *item;
151 FILE *fp;
152
153 fp = fopen(filename,"w");
154 if (NULL == fp) {
155 fprintf(stderr,"open %s: %s\n",filename,strerror(errno));
156 return;
157 }
158 list_for_each(item, &h->files) {
159 file = list_entry(item, struct file_button, window);
160 fprintf(fp,"%s\n",file->filename);
161 }
162 fclose(fp);
163 filelist_file(h,filename);
164 }
165
filelist_delall(struct list_handle * h)166 static void filelist_delall(struct list_handle *h)
167 {
168 struct file_button *file;
169 struct list_head *item;
170
171 list_for_each(item, &h->files) {
172 file = list_entry(item, struct file_button, window);
173 XtUnmanageChild(file->widget);
174 XtDestroyWidget(file->widget);
175 }
176 }
177
178 /* ---------------------------------------------------------------------- */
179 /* receive data (drops, paste) */
180
181 static Atom targets[16];
182 static Cardinal ntargets;
183
184 static void
filelist_xfer(Widget widget,XtPointer clientdata,XtPointer call_data)185 filelist_xfer(Widget widget, XtPointer clientdata, XtPointer call_data)
186 {
187 struct list_handle *h = clientdata;
188 XmSelectionCallbackStruct *scs = call_data;
189 unsigned char *cdata = scs->value;
190 unsigned long *ldata = scs->value;
191 Atom target = 0;
192 unsigned int i,j,pending;
193 char *file;
194
195 if (debug) {
196 char *y = !scs->type ? NULL : XGetAtomName(dpy,scs->type);
197 char *t = !scs->target ? NULL : XGetAtomName(dpy,scs->target);
198 char *s = !scs->selection ? NULL : XGetAtomName(dpy,scs->selection);
199 fprintf(stderr,"list: id=%p target=%s type=%s selection=%s\n",
200 scs->transfer_id,t,y,s);
201 if (y) XFree(y);
202 if (t) XFree(t);
203 if (s) XFree(s);
204 }
205
206 pending = scs->remaining;
207 if (scs->target == XA_TARGETS) {
208 /* look if we find a target we can deal with ... */
209 for (i = 0; !target && i < scs->length; i++) {
210 for (j = 0; j < ntargets; j++) {
211 if (ldata[i] == targets[j]) {
212 target = ldata[i];
213 break;
214 }
215 }
216 }
217 if (target) {
218 XmTransferValue(scs->transfer_id, target, filelist_xfer,
219 clientdata, XtLastTimestampProcessed(dpy));
220 pending++;
221 }
222 if (debug) {
223 fprintf(stderr,"list: available targets: ");
224 for (i = 0; i < scs->length; i++) {
225 char *name = !ldata[i] ? NULL : XGetAtomName(dpy,ldata[i]);
226 fprintf(stderr,"%s%s", i != 0 ? ", " : "", name);
227 XFree(name);
228 }
229 fprintf(stderr,"\n");
230 if (0 == scs->length)
231 fprintf(stderr,"list: Huh? no TARGETS available?\n");
232 }
233 }
234
235 if (scs->target == XA_FILE_NAME ||
236 scs->target == XA_FILE) {
237 /* load file */
238 if (debug)
239 fprintf(stderr,"list: => \"%s\"\n",cdata);
240 filelist_add(h,cdata);
241 }
242
243 if (scs->target == _NETSCAPE_URL) {
244 /* load file */
245 if (debug)
246 fprintf(stderr,"list: => \"%s\"\n",cdata);
247 filelist_add(h,cdata);
248 }
249
250 if (scs->target == MIME_TEXT_URI_LIST) {
251 /* load file(s) */
252 for (file = strtok(cdata,"\r\n");
253 NULL != file;
254 file = strtok(NULL,"\r\n")) {
255 if (debug)
256 fprintf(stderr,"list: => \"%s\"\n",file);
257 filelist_add(h,file);
258 }
259 }
260
261 XFree(scs->value);
262 if (1 == pending) {
263 /* all done -- clean up */
264 if (debug)
265 fprintf(stderr,"list: all done\n");
266 XmTransferDone(scs->transfer_id, XmTRANSFER_DONE_SUCCEED);
267 XdndDropFinished(widget,scs);
268 }
269 }
270
271 static void
filelist_dest_cb(Widget w,XtPointer clientdata,XtPointer call_data)272 filelist_dest_cb(Widget w, XtPointer clientdata, XtPointer call_data)
273 {
274 XmDestinationCallbackStruct *dcs = call_data;
275
276 if (debug)
277 fprintf(stderr,"list: xfer id=%p\n",dcs->transfer_id);
278 XmTransferValue(dcs->transfer_id, XA_TARGETS, filelist_xfer,
279 clientdata, XtLastTimestampProcessed(dpy));
280 }
281
282 /*----------------------------------------------------------------------*/
283
284 static void
filelist_new_cb(Widget widget,XtPointer clientdata,XtPointer call_data)285 filelist_new_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
286 {
287 struct list_handle *h = clientdata;
288
289 filelist_delall(h);
290 if (h->filename) {
291 free(h->filename);
292 h->filename = NULL;
293 }
294 XtVaSetValues(h->shell,XtNtitle,"new list",NULL);
295 }
296
297 static void
init_file_box(Widget box,char * filename)298 init_file_box(Widget box, char *filename)
299 {
300 char *dir,*file;
301 XmString s1;
302
303 if (NULL == filename) {
304 dir = strdup(ida_lists);
305 } else {
306 dir = strdup(filename);
307 file = strrchr(dir,'/');
308 if (NULL == file)
309 return;
310 *file = 0;
311 file++;
312 }
313
314 s1 = XmStringGenerate(dir, NULL, XmMULTIBYTE_TEXT, NULL);
315 XtVaSetValues(box,
316 XmNdirectory, s1,
317 XmNpattern, NULL,
318 NULL);
319 XmFileSelectionDoSearch(box,NULL);
320 XmStringFree(s1);
321 free(dir);
322 }
323
324 static void
load_done_cb(Widget widget,XtPointer clientdata,XtPointer call_data)325 load_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
326 {
327 XmFileSelectionBoxCallbackStruct *cb = call_data;
328 struct list_handle *h = clientdata;
329 char *filename;
330
331 if (cb->reason == XmCR_OK) {
332 filename = XmStringUnparse(cb->value,NULL,
333 XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
334 NULL,0,0);
335 if (debug)
336 fprintf(stderr,"read list from %s\n",filename);
337 filelist_read(h, filename);
338 }
339 XtUnmanageChild(widget);
340 }
341
342 static void
filelist_load_cb(Widget widget,XtPointer clientdata,XtPointer call_data)343 filelist_load_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
344 {
345 struct list_handle *h = clientdata;
346 Widget help;
347
348 if (NULL == h->loadbox) {
349 h->loadbox = XmCreateFileSelectionDialog(h->shell,"load",NULL,0);
350 help = XmFileSelectionBoxGetChild(h->loadbox,XmDIALOG_HELP_BUTTON);
351 XtUnmanageChild(help);
352 XtAddCallback(h->loadbox,XmNokCallback,load_done_cb,h);
353 XtAddCallback(h->loadbox,XmNcancelCallback,load_done_cb,h);
354 }
355 init_file_box(h->loadbox,h->filename);
356 XtManageChild(h->loadbox);
357 }
358
359 static void
save_done_cb(Widget widget,XtPointer clientdata,XtPointer call_data)360 save_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
361 {
362 XmFileSelectionBoxCallbackStruct *cb = call_data;
363 struct list_handle *h = clientdata;
364 char *filename;
365
366 if (cb->reason == XmCR_OK) {
367 filename = XmStringUnparse(cb->value,NULL,
368 XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
369 NULL,0,0);
370 if (debug)
371 fprintf(stderr,"write list to %s\n",filename);
372 filelist_write(h, filename);
373 }
374 XtUnmanageChild(widget);
375 }
376
377 static void
filelist_save_as_cb(Widget widget,XtPointer clientdata,XtPointer call_data)378 filelist_save_as_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
379 {
380 struct list_handle *h = clientdata;
381 Widget help;
382
383 if (NULL == h->savebox) {
384 h->savebox = XmCreateFileSelectionDialog(h->shell,"save",NULL,0);
385 help = XmFileSelectionBoxGetChild(h->savebox,XmDIALOG_HELP_BUTTON);
386 XtUnmanageChild(help);
387
388 XtAddCallback(h->savebox,XmNokCallback,save_done_cb,h);
389 XtAddCallback(h->savebox,XmNcancelCallback,save_done_cb,h);
390 }
391 init_file_box(h->savebox,h->filename);
392 XtManageChild(h->savebox);
393 }
394
395 static void
filelist_save_cb(Widget widget,XtPointer clientdata,XtPointer call_data)396 filelist_save_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
397 {
398 struct list_handle *h = clientdata;
399
400 if (h->filename) {
401 filelist_write(h, h->filename);
402 } else {
403 filelist_save_as_cb(widget, h, call_data);
404 }
405 }
406
407 static void
filelist_destroy(Widget widget,XtPointer clientdata,XtPointer call_data)408 filelist_destroy(Widget widget, XtPointer clientdata, XtPointer call_data)
409 {
410 struct list_handle *h = clientdata;
411
412 if (h->filename)
413 free(h->filename);
414 ptr_unregister(h->shell);
415 free(h);
416 }
417
418 static void
filelist_list_load(Widget widget,XtPointer clientdata,XtPointer call_data)419 filelist_list_load(Widget widget, XtPointer clientdata, XtPointer call_data)
420 {
421 struct list_handle *h = clientdata;
422
423 filelist_delall(h);
424 filelist_read(h, XtName(widget));
425 }
426
filelist_builddir(Widget menu,char * path,XtPointer clientdata)427 static void filelist_builddir(Widget menu, char *path, XtPointer clientdata)
428 {
429 Widget push,submenu;
430 XmString str;
431 char filename[1024];
432 struct dirent *ent;
433 struct stat st;
434 DIR *dir;
435
436 dir = opendir(path);
437 while (NULL != (ent = readdir(dir))) {
438 if (ent->d_name[0] == '.')
439 continue;
440 snprintf(filename,sizeof(filename),"%s/%s",
441 path,ent->d_name);
442 if (-1 == lstat(filename,&st))
443 continue;
444
445 str = XmStringGenerate(ent->d_name,NULL, XmMULTIBYTE_TEXT,NULL);
446 if (S_ISREG(st.st_mode)) {
447 push = XtVaCreateManagedWidget(filename,
448 xmPushButtonWidgetClass,menu,
449 XmNlabelString,str,
450 NULL);
451 XtAddCallback(push,XmNactivateCallback,filelist_list_load,clientdata);
452 }
453 if (S_ISDIR(st.st_mode)) {
454 submenu = XmCreatePulldownMenu(menu,"subdirM",NULL,0);
455 XtVaCreateManagedWidget("subdir",xmCascadeButtonWidgetClass,menu,
456 XmNlabelString,str,
457 XmNsubMenuId,submenu,
458 NULL);
459 filelist_builddir(submenu,filename,clientdata);
460 }
461 XmStringFree(str);
462 }
463 closedir(dir);
464 }
465
466 static void
filelist_lists(Widget widget,XtPointer clientdata,XtPointer call_data)467 filelist_lists(Widget widget, XtPointer clientdata, XtPointer call_data)
468 {
469 WidgetList children,list;
470 Cardinal nchildren;
471 int i;
472
473 XtVaGetValues(widget,
474 XtNchildren,&children,
475 XtNnumChildren,&nchildren,
476 NULL);
477 list = malloc(sizeof(Widget*)*nchildren);
478 memcpy(list,children,sizeof(Widget*)*nchildren);
479 for (i = 0; i < nchildren; i++)
480 XtDestroyWidget(list[i]);
481 free(list);
482
483 filelist_builddir(widget,ida_lists,clientdata);
484 }
485
486 static void
filelist_action_cb(Widget widget,XtPointer clientdata,XtPointer call_data)487 filelist_action_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
488 {
489 XmContainerSelectCallbackStruct *cd = call_data;
490 char *file;
491
492 if (XmCR_DEFAULT_ACTION == cd->reason && 1 == cd->selected_item_count) {
493 file = XtName(cd->selected_items[0]);
494 if (debug)
495 fprintf(stderr,"browser: action %s\n", file);
496 new_file(file,1);
497 }
498 }
499
500 /*----------------------------------------------------------------------*/
501
502 void
filelist_window(void)503 filelist_window(void)
504 {
505 Widget form,clip,menubar,menu,push;
506 struct list_handle *h;
507 Arg args[8];
508 int n = 0;
509
510 if (0 == ntargets) {
511 /* first time init */
512 targets[ntargets++] = MIME_TEXT_URI_LIST;
513 targets[ntargets++] = XA_FILE_NAME;
514 targets[ntargets++] = XA_FILE;
515 targets[ntargets++] = _NETSCAPE_URL;
516 }
517
518 h = malloc(sizeof(*h));
519 if (NULL == h) {
520 fprintf(stderr,"out of memory");
521 return;
522 }
523 memset(h,0,sizeof(*h));
524 INIT_LIST_HEAD(&h->files);
525
526 h->shell = XtVaAppCreateShell("filelist","Ida",
527 topLevelShellWidgetClass,
528 dpy,
529 XtNclientLeader,app_shell,
530 XmNdeleteResponse,XmDESTROY,
531 XtNtitle,"new list",
532 NULL);
533 XmdRegisterEditres(h->shell);
534 XtAddCallback(h->shell,XtNdestroyCallback,filelist_destroy,h);
535
536 /* widgets */
537 form = XtVaCreateManagedWidget("form", xmFormWidgetClass, h->shell,
538 NULL);
539 menubar = XmCreateMenuBar(form,"cbar",NULL,0);
540 XtManageChild(menubar);
541 h->status = XtVaCreateManagedWidget("status",xmLabelWidgetClass, form,
542 NULL);
543
544 /* scrolled container */
545 h->details[0] = XmStringGenerate("Image", NULL, XmMULTIBYTE_TEXT,NULL);
546 h->details[DETAIL_SIZE+1] =
547 XmStringGenerate("Size", NULL, XmMULTIBYTE_TEXT,NULL);
548 h->details[DETAIL_COMMENT+1] =
549 XmStringGenerate("Comment", NULL, XmMULTIBYTE_TEXT,NULL);
550 XtSetArg(args[n], XmNdetailColumnHeading, h->details); n++;
551 XtSetArg(args[n], XmNdetailColumnHeadingCount, DETAIL_COUNT+1); n++;
552
553 h->scroll = XmCreateScrolledWindow(form, "scroll", NULL, 0);
554 XtManageChild(h->scroll);
555 h->container = XmCreateContainer(h->scroll,"container",
556 args,n);
557 XtManageChild(h->container);
558 XdndDropSink(h->container);
559
560 XtAddCallback(h->scroll, XmNtraverseObscuredCallback,
561 container_traverse_cb, NULL);
562 XtAddCallback(h->container,XmNdefaultActionCallback,
563 filelist_action_cb,h);
564 XtAddCallback(h->container,XmNconvertCallback,
565 container_convert_cb,h);
566 XtAddCallback(h->container,XmNdestinationCallback,
567 filelist_dest_cb,h);
568
569 XtVaGetValues(h->scroll,XmNclipWindow,&clip,NULL);
570 XtAddEventHandler(clip,StructureNotifyMask,True,container_resize_eh,NULL);
571
572 /* menu - file */
573 menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0);
574 XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar,
575 XmNsubMenuId,menu,NULL);
576 push = XtVaCreateManagedWidget("new",xmPushButtonWidgetClass,menu,NULL);
577 XtAddCallback(push,XmNactivateCallback,filelist_new_cb,h);
578 push = XtVaCreateManagedWidget("load",xmPushButtonWidgetClass,menu,NULL);
579 XtAddCallback(push,XmNactivateCallback,filelist_load_cb,h);
580 push = XtVaCreateManagedWidget("save",xmPushButtonWidgetClass,menu,NULL);
581 XtAddCallback(push,XmNactivateCallback,filelist_save_cb,h);
582 push = XtVaCreateManagedWidget("saveas",xmPushButtonWidgetClass,menu,NULL);
583 XtAddCallback(push,XmNactivateCallback,filelist_save_as_cb,h);
584
585 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
586 push = XtVaCreateManagedWidget("close",xmPushButtonWidgetClass,menu,NULL);
587 XtAddCallback(push,XmNactivateCallback,destroy_cb,h->shell);
588
589 /* menu - edit */
590 menu = XmCreatePulldownMenu(menubar,"editM",NULL,0);
591 XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar,
592 XmNsubMenuId,menu,NULL);
593 container_menu_edit(menu,h->container, 0,1,1,1);
594
595 /* menu - view */
596 menu = XmCreatePulldownMenu(menubar,"viewM",NULL,0);
597 XtVaCreateManagedWidget("view",xmCascadeButtonWidgetClass,menubar,
598 XmNsubMenuId,menu,NULL);
599 container_menu_view(menu,h->container);
600
601 /* menu - lists */
602 menu = XmCreatePulldownMenu(menubar,"listsM",NULL,0);
603 XtVaCreateManagedWidget("lists",xmCascadeButtonWidgetClass,menubar,
604 XmNsubMenuId,menu,NULL);
605 XtAddCallback(menu, XmNmapCallback, filelist_lists, h);
606
607 /* read dir and show window */
608 container_detail_cb(NULL,h->container,NULL);
609 XtPopup(h->shell,XtGrabNone);
610 ptr_register(h->shell);
611 }
612
613 void
filelist_ac(Widget widget,XEvent * event,String * params,Cardinal * num_params)614 filelist_ac(Widget widget, XEvent *event,
615 String *params, Cardinal *num_params)
616 {
617 filelist_window();
618 }
619