aboutsummaryrefslogtreecommitdiffstats
path: root/ida.c
diff options
context:
space:
mode:
Diffstat (limited to 'ida.c')
-rw-r--r--ida.c1909
1 files changed, 1909 insertions, 0 deletions
diff --git a/ida.c b/ida.c
new file mode 100644
index 0000000..65e1d0a
--- /dev/null
+++ b/ida.c
@@ -0,0 +1,1909 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <locale.h>
+#include <langinfo.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <Xm/Xm.h>
+#include <Xm/Primitive.h>
+#include <Xm/Label.h>
+#include <Xm/CascadeB.h>
+#include <Xm/PushB.h>
+#include <Xm/Separator.h>
+#include <Xm/RowColumn.h>
+#include <Xm/ScrolledW.h>
+#include <Xm/Protocols.h>
+#include <Xm/List.h>
+#include <Xm/Form.h>
+#include <Xm/MessageB.h>
+#include <Xm/SelectioB.h>
+#include <Xm/Scale.h>
+#include <Xm/Text.h>
+#include <Xm/FileSB.h>
+#include <Xm/ToggleB.h>
+#include <Xm/DrawingA.h>
+#include <Xm/Transfer.h>
+#include <Xm/TransferP.h>
+
+#include "RegEdit.h"
+#include "ida.h"
+#include "x11.h"
+#include "man.h"
+#include "readers.h"
+#include "writers.h"
+#include "viewer.h"
+#include "op.h"
+#include "lut.h"
+#include "filter.h"
+#include "color.h"
+#include "icons.h"
+#include "browser.h"
+#include "filelist.h"
+#include "xdnd.h"
+#include "selections.h"
+#include "sane.h"
+#include "curl.h"
+#include "idaconfig.h"
+
+/* ---------------------------------------------------------------------- */
+
+static void popup_ac(Widget, XEvent*, String*, Cardinal*);
+static void exit_ac(Widget, XEvent*, String*, Cardinal*);
+static void next_ac(Widget, XEvent*, String*, Cardinal*);
+static void prev_ac(Widget, XEvent*, String*, Cardinal*);
+static void next_page_ac(Widget, XEvent*, String*, Cardinal*);
+static void prev_page_ac(Widget, XEvent*, String*, Cardinal*);
+static void zoom_ac(Widget, XEvent*, String*, Cardinal*);
+static void scroll_ac(Widget, XEvent*, String*, Cardinal*);
+static void debug_ac(Widget, XEvent*, String*, Cardinal*);
+static void load_ac(Widget, XEvent*, String*, Cardinal*);
+static void save_ac(Widget, XEvent*, String*, Cardinal*);
+static void scan_ac(Widget, XEvent*, String*, Cardinal*);
+static void print_ac(Widget, XEvent*, String*, Cardinal*);
+
+static void undo_ac(Widget, XEvent*, String*, Cardinal*);
+static void filter_ac(Widget, XEvent*, String*, Cardinal*);
+static void gamma_ac(Widget, XEvent*, String*, Cardinal*);
+static void bright_ac(Widget, XEvent*, String*, Cardinal*);
+static void contrast_ac(Widget, XEvent*, String*, Cardinal*);
+static void color_ac(Widget, XEvent*, String*, Cardinal*);
+static void f3x3_ac(Widget, XEvent*, String*, Cardinal*);
+static void resize_ac(Widget, XEvent*, String*, Cardinal*);
+static void rotate_ac(Widget, XEvent*, String*, Cardinal*);
+static void sharpe_ac(Widget, XEvent*, String*, Cardinal*);
+
+static XtActionsRec actionTable[] = {
+ { "Exit", exit_ac },
+ { "Next", next_ac },
+ { "Prev", prev_ac },
+ { "NextPage", next_page_ac },
+ { "PrevPage", prev_page_ac },
+ { "Zoom", zoom_ac },
+ { "Scroll", scroll_ac },
+ { "Debug", debug_ac },
+ { "Popup", popup_ac },
+ { "Man", man_action },
+ { "Load", load_ac },
+ { "Save", save_ac },
+ { "Scan", scan_ac },
+ { "Print", print_ac },
+ { "Browser", browser_ac },
+ { "Filelist", filelist_ac },
+
+ { "Undo", undo_ac },
+ { "Filter", filter_ac },
+ { "Gamma", gamma_ac },
+ { "Bright", bright_ac },
+ { "Contrast", contrast_ac },
+ { "Color", color_ac },
+ { "F3x3", f3x3_ac },
+ { "Resize", resize_ac },
+ { "Rotate", rotate_ac },
+ { "Sharpe", sharpe_ac },
+
+ { "Ipc", ipc_ac },
+ { "Xdnd", XdndAction },
+};
+
+/* ---------------------------------------------------------------------- */
+
+XtAppContext app_context;
+Display *dpy;
+Widget app_shell;
+int gray=0;
+char *binary;
+struct ida_viewer *ida;
+
+/* ---------------------------------------------------------------------- */
+
+struct ARGS args;
+unsigned int pcd_res;
+unsigned int sane_res;
+
+XtResource args_desc[] = {
+ {
+ "debug",
+ XtCBoolean, XtRBoolean, sizeof(Boolean),
+ XtOffset(struct ARGS*,debug),
+ XtRString, "false"
+ },{
+ "help",
+ XtCBoolean, XtRBoolean, sizeof(Boolean),
+ XtOffset(struct ARGS*,help),
+ XtRString, "false"
+ },{
+ "testload",
+ XtCBoolean, XtRBoolean, sizeof(Boolean),
+ XtOffset(struct ARGS*,testload),
+ XtRString, "false"
+ }
+};
+const int args_count = XtNumber(args_desc);
+
+XrmOptionDescRec opt_desc[] = {
+ { "-d", "debug", XrmoptionNoArg, "true" },
+ { "-debug", "debug", XrmoptionNoArg, "true" },
+ { "-testload", "testload", XrmoptionNoArg, "true" },
+ { "-h", "help", XrmoptionNoArg, "true" },
+ { "-help", "help", XrmoptionNoArg, "true" },
+ { "--help", "help", XrmoptionNoArg, "true" },
+};
+const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec));
+
+static String fallback_ressources[] = {
+#include "Ida.ad.h"
+ NULL
+};
+
+/* ---------------------------------------------------------------------- */
+
+static struct ida_writer *cwriter;
+static char *save_filename;
+static char *print_command;
+
+static Widget control_shell,status;
+static Atom wm_delete_window;
+
+static Widget view,loadbox,savebox,printbox;
+
+/* file list */
+static Widget wlist;
+static char **files = NULL;
+static int cfile = -1;
+static int nfiles = 0;
+static int cpage = 0;
+static int npages = 1;
+
+/* filter controls */
+static int gamma_val = 100;
+static int bright_val = 0;
+static int contrast_val = 0;
+static int rotate_val = 0;
+static int sharpe_val = 10;
+
+static struct MY_TOPLEVELS {
+ char *name;
+ Widget *shell;
+ int mapped;
+} my_toplevels [] = {
+ { "control", &control_shell },
+};
+#define TOPLEVELS (sizeof(my_toplevels)/sizeof(struct MY_TOPLEVELS))
+
+/* ---------------------------------------------------------------------- */
+
+static void
+popup_ac(Widget widget, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ unsigned int i;
+
+ /* which window we are talking about ? */
+ if (*num_params > 0) {
+ for (i = 0; i < TOPLEVELS; i++) {
+ if (0 == strcasecmp(my_toplevels[i].name,params[0]))
+ break;
+ }
+ if (i == TOPLEVELS) {
+ fprintf(stderr,"PopupAction: oops: shell not found (name=%s)\n",
+ params[0]);
+ return;
+ }
+ } else {
+ for (i = 0; i < TOPLEVELS; i++) {
+ if (*(my_toplevels[i].shell) == widget)
+ break;
+ }
+ if (i == TOPLEVELS) {
+ fprintf(stderr,"PopupAction: oops: shell not found (%p:%s)\n",
+ widget,XtName(widget));
+ return;
+ }
+ }
+
+ /* popup/down window */
+ if (!my_toplevels[i].mapped) {
+ XtPopup(*(my_toplevels[i].shell), XtGrabNone);
+ my_toplevels[i].mapped = 1;
+ } else {
+ XtPopdown(*(my_toplevels[i].shell));
+ my_toplevels[i].mapped = 0;
+ }
+}
+
+static void
+popupdown_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ int i = 0;
+ popup_ac(clientdata, NULL, NULL, &i);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void
+destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ XtDestroyWidget(clientdata);
+}
+
+void
+action_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ char *calls, *action, *argv[16]; /* max: F3x3(9 args) */
+ int argc;
+
+ calls = strdup(clientdata);
+ action = strtok(calls,"(),");
+ for (argc = 0; NULL != (argv[argc] = strtok(NULL,"(),")); argc++)
+ /* nothing */;
+ XtCallActionProc(widget,action,NULL,argv,argc);
+ free(calls);
+}
+
+static void
+about_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ Widget msgbox;
+
+ msgbox = XmCreateInformationDialog(app_shell,"aboutbox",NULL,0);
+ XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON));
+ XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON));
+ XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox);
+ XtManageChild(msgbox);
+}
+
+#if 0
+static void
+sorry_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ Widget msgbox;
+
+ msgbox = XmCreateErrorDialog(app_shell,"sorrybox",NULL,0);
+ XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON));
+ XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON));
+ XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox);
+ XtManageChild(msgbox);
+}
+#endif
+
+void
+debug_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ unsigned int i;
+
+ fprintf(stderr,"Debug:");
+ for (i = 0; i < *num; i++)
+ fprintf(stderr," %s",params[i]);
+ fprintf(stderr,"\n");
+}
+
+static void
+display_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ XmDisplayCallbackStruct *arg = call_data;
+
+ switch (arg->reason) {
+ case XmCR_NO_RENDITION:
+ fprintf(stderr,"display_cb: no rendition: \"%s\"\n",arg->tag);
+ break;
+ case XmCR_NO_FONT:
+ fprintf(stderr,"display_cb: no font: \"%s\"\n",arg->font_name);
+ break;
+ default:
+ /* should not happen */
+ fprintf(stderr,"display_cb: unknown reason [%d]\n",arg->reason);
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct ptr_list {
+ struct ptr_list *next;
+ Widget widget;
+};
+struct ptr_list *ptr_head;
+
+void ptr_register(Widget widget)
+{
+ struct ptr_list *item;
+
+ if (XtWindow(widget))
+ XDefineCursor(XtDisplay(widget), XtWindow(widget),
+ ptrs[POINTER_NORMAL]);
+ item = malloc(sizeof(*item));
+ memset(item,0,sizeof(*item));
+ item->widget = widget;
+ item->next = ptr_head;
+ ptr_head = item;
+}
+
+void ptr_unregister(Widget widget)
+{
+ struct ptr_list *item,*fitem;
+
+ if (ptr_head->widget == widget) {
+ fitem = ptr_head;
+ ptr_head = ptr_head->next;
+ free(fitem);
+ return;
+ }
+ for (item = ptr_head; NULL != item->next; item = item->next) {
+ if (item->next->widget == widget) {
+ fitem = item->next;
+ item->next = fitem->next;
+ free(fitem);
+ return;
+ }
+ }
+ /* shouldn't happen */
+ fprintf(stderr,"Oops: widget not found in list\n");
+}
+
+void ptr_busy(void)
+{
+ struct ptr_list *item;
+
+ for (item = ptr_head; NULL != item; item = item->next) {
+ if (!XtWindow(item->widget))
+ continue;
+ XDefineCursor(XtDisplay(item->widget), XtWindow(item->widget),
+ ptrs[POINTER_BUSY]);
+ }
+ XSync(dpy,False);
+}
+
+void ptr_idle(void)
+{
+ struct ptr_list *item;
+
+ for (item = ptr_head; NULL != item; item = item->next) {
+ if (!XtWindow(item->widget))
+ continue;
+ XDefineCursor(XtDisplay(item->widget), XtWindow(item->widget),
+ ptrs[POINTER_NORMAL]);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static Boolean
+exit_wp(XtPointer client_data)
+{
+ exit(0);
+}
+
+static void
+exit_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ XtAppAddWorkProc(app_context,exit_wp, NULL);
+ XtDestroyWidget(app_shell);
+}
+
+void
+exit_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ exit_cb(widget,NULL,NULL);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+list_update(void)
+{
+ XmStringTable tab;
+ int i;
+
+ tab = malloc(nfiles * sizeof(XmString));
+ for (i = 0; i < nfiles; i++)
+ tab[i] = XmStringGenerate(files[i], NULL, XmMULTIBYTE_TEXT, NULL);
+ XtVaSetValues(wlist,
+ XmNitems, tab,
+ XmNitemCount, nfiles,
+ NULL);
+ for (i = 0; i < nfiles; i++)
+ XmStringFree(tab[i]);
+ free(tab);
+}
+
+static int
+list_append(char *filename)
+{
+ int i;
+
+ for (i = 0; i < nfiles; i++)
+ if (0 == strcmp(files[i],filename))
+ return i;
+ files = realloc(files,sizeof(char*)*(nfiles+1));
+ files[nfiles] = strdup(filename);
+ nfiles++;
+ return nfiles-1;
+}
+
+static void
+list_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ XmListCallbackStruct *list = calldata;
+
+ if (0 == list->selected_item_count)
+ return;
+ cfile = list->selected_item_positions[0]-1;
+ cpage = 0;
+ npages = viewer_loadimage(ida,files[cfile],cpage);
+ if (-1 == npages)
+ return;
+ resize_shell();
+}
+
+static void
+pcd_set(Widget widget)
+{
+ WidgetList items;
+ Cardinal nitems;
+ unsigned int i;
+ int value;
+
+ value = GET_PHOTOCD_RES();
+ XtVaGetValues(widget,XtNchildren,&items,
+ XtNnumChildren,&nitems,NULL);
+ for (i = 0; i < nitems; i++)
+ XmToggleButtonSetState(items[i],value == i+1,False);
+ pcd_res = value;
+}
+
+static void
+pcd_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ cfg_set_int(O_PHOTOCD_RES,(intptr_t)client_data);
+ pcd_set(XtParent(widget));
+}
+
+static void
+cfg_bool_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ char *option = XtName(widget);
+ Boolean value = XmToggleButtonGetState(widget);
+ cfg_set_bool(O_OPTIONS, option, value);
+}
+
+static void
+cfg_save_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ ida_write_config();
+}
+
+static void
+create_control(void)
+{
+ Widget form,menubar,tool,menu,smenu,push;
+
+ control_shell = XtVaAppCreateShell("ctrl","Iv",
+ topLevelShellWidgetClass,
+ dpy,
+ XtNclientLeader,app_shell,
+ XmNdeleteResponse,XmDO_NOTHING,
+ NULL);
+ XmdRegisterEditres(control_shell);
+ XmAddWMProtocolCallback(control_shell,wm_delete_window,
+ popupdown_cb,control_shell);
+
+ /* widgets */
+ form = XtVaCreateManagedWidget("form", xmFormWidgetClass, control_shell,
+ NULL);
+ menubar = XmCreateMenuBar(form,"bar",NULL,0);
+ XtManageChild(menubar);
+ tool = XtVaCreateManagedWidget("tool",xmRowColumnWidgetClass, form,
+ NULL);
+ status = XtVaCreateManagedWidget("status", xmLabelWidgetClass, form,
+ NULL);
+ wlist = XmCreateScrolledList(form,"list",NULL,0);
+ XtManageChild(wlist);
+ XtAddCallback(wlist,XmNdefaultActionCallback,list_cb,NULL);
+ XtAddCallback(wlist,XmNdestinationCallback,selection_dest,NULL);
+ dnd_add(wlist);
+
+ /* menu - file */
+ menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0);
+ XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar,
+ XmNsubMenuId,menu,NULL);
+ push = XtVaCreateManagedWidget("load",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Load()");
+ push = XtVaCreateManagedWidget("save",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Save()");
+ push = XtVaCreateManagedWidget("browse",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Browser()");
+ push = XtVaCreateManagedWidget("filelist",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filelist()");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+#ifdef HAVE_LIBSANE
+ sane_menu(menu);
+#endif
+ push = XtVaCreateManagedWidget("print",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Print()");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("quit",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,exit_cb,NULL);
+
+ /* menu - edit */
+ menu = XmCreatePulldownMenu(menubar,"editM",NULL,0);
+ XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar,
+ XmNsubMenuId,menu,NULL);
+ push = XtVaCreateManagedWidget("undo",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Undo()");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("copy",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Ipc(copy)");
+ push = XtVaCreateManagedWidget("paste",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Ipc(paste)");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("flipv",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-vert)");
+ push = XtVaCreateManagedWidget("fliph",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-horz)");
+ push = XtVaCreateManagedWidget("rotcw",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-cw)");
+ push = XtVaCreateManagedWidget("rotccw",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-ccw)");
+ push = XtVaCreateManagedWidget("invert",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(invert)");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("crop",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(crop)");
+ push = XtVaCreateManagedWidget("acrop",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(autocrop)");
+ push = XtVaCreateManagedWidget("scale",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Resize()");
+ push = XtVaCreateManagedWidget("rotany",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Rotate()");
+
+ /* menu - filters / operations */
+ menu = XmCreatePulldownMenu(menubar,"opM",NULL,0);
+ XtVaCreateManagedWidget("op",xmCascadeButtonWidgetClass,menubar,
+ XmNsubMenuId,menu,NULL);
+ push = XtVaCreateManagedWidget("gamma",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Gamma()");
+ push = XtVaCreateManagedWidget("bright",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Bright()");
+ push = XtVaCreateManagedWidget("contr",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Contrast()");
+ push = XtVaCreateManagedWidget("color",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Color()");
+ push = XtVaCreateManagedWidget("gray",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(grayscale)");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("blur",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,
+ "F3x3(1,1,1, 1,1,1, 1,1,1, 1,9,0)");
+ push = XtVaCreateManagedWidget("sharpe",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Sharpe()");
+ push = XtVaCreateManagedWidget("edge",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,
+ "F3x3(-1,-1,-1, -1,8,-1, -1,-1,-1)");
+ push = XtVaCreateManagedWidget("emboss",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,
+ "F3x3(1,0,0, 0,0,0, 0,0,-1, 0,0,128)");
+
+ /* menu - view */
+ menu = XmCreatePulldownMenu(menubar,"viewM",NULL,0);
+ XtVaCreateManagedWidget("view",xmCascadeButtonWidgetClass,menubar,
+ XmNsubMenuId,menu,NULL);
+ push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Prev()");
+ push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Next()");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("prevpage",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"PrevPage()");
+ push = XtVaCreateManagedWidget("nextpage",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"NextPage()");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("zoomin",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(inc)");
+ push = XtVaCreateManagedWidget("zoomout",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(dec)");
+
+ /* menu - options */
+ menu = XmCreatePulldownMenu(menubar,"optM",NULL,0);
+ push = XtVaCreateManagedWidget("opt",xmCascadeButtonWidgetClass,menubar,
+ XmNsubMenuId,menu,NULL);
+ smenu = XmCreatePulldownMenu(menu,"pcdM",NULL,0);
+ XtVaCreateManagedWidget("pcd",xmCascadeButtonWidgetClass,menu,
+ XmNsubMenuId,smenu,NULL);
+ push = XtVaCreateManagedWidget("1",xmToggleButtonWidgetClass,smenu,NULL);
+ XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)1);
+ push = XtVaCreateManagedWidget("2",xmToggleButtonWidgetClass,smenu,NULL);
+ XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)2);
+ push = XtVaCreateManagedWidget("3",xmToggleButtonWidgetClass,smenu,NULL);
+ XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)3);
+ push = XtVaCreateManagedWidget("4",xmToggleButtonWidgetClass,smenu,NULL);
+ XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)4);
+ push = XtVaCreateManagedWidget("5",xmToggleButtonWidgetClass,smenu,NULL);
+ XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)5);
+ pcd_set(smenu);
+
+ push = XtVaCreateManagedWidget("autozoom",xmToggleButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNvalueChangedCallback,cfg_bool_cb,NULL);
+ XmToggleButtonSetState(push,GET_AUTOZOOM(),False);
+
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("cfgsave",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,cfg_save_cb,NULL);
+
+ /* menu - help */
+ menu = XmCreatePulldownMenu(menubar,"helpM",NULL,0);
+ push = XtVaCreateManagedWidget("help",xmCascadeButtonWidgetClass,menubar,
+ XmNsubMenuId,menu,NULL);
+ XtVaSetValues(menubar,XmNmenuHelpWidget,push,NULL);
+ push = XtVaCreateManagedWidget("man",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Man(ida)");
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
+ push = XtVaCreateManagedWidget("about",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,about_cb,NULL);
+
+ /* toolbar */
+ push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Prev()");
+ push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Next()");
+ push = XtVaCreateManagedWidget("zoomin",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(inc)");
+ push = XtVaCreateManagedWidget("zoomout",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(dec)");
+
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL);
+ push = XtVaCreateManagedWidget("flipv",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-vert)");
+ push = XtVaCreateManagedWidget("fliph",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-horz)");
+ push = XtVaCreateManagedWidget("rotccw",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-ccw)");
+ push = XtVaCreateManagedWidget("rotcw",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-cw)");
+
+ XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL);
+ push = XtVaCreateManagedWidget("exit",xmPushButtonWidgetClass,tool,NULL);
+ XtAddCallback(push,XmNactivateCallback,exit_cb,NULL);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void
+resize_shell(void)
+{
+ char *title,*base;
+ Dimension x,y,w,h,sw,sh;
+ XmString str;
+ int len;
+
+ XtVaGetValues(app_shell, XtNx,&x, XtNy,&y, NULL);
+
+ /* resize shell + move shell
+ size: image size + 2*shadowThickness */
+ w = ida->scrwidth+2;
+ h = ida->scrheight+2;
+ sw = XtScreen(ida->widget)->width;
+ sh = XtScreen(ida->widget)->height;
+ if (w > sw)
+ w = sw;
+ if (h > sh)
+ h = sh;
+ if (x+w > sw)
+ x = sw-w;
+ if (y+h > sh)
+ y = sh-h;
+
+ base = strrchr(ida->file,'/');
+ if (base)
+ base++;
+ else
+ base = ida->file;
+ title = malloc(strlen(base)+128);
+ len = sprintf(title,"%s (%dx%d", base,
+ ida->img.i.width, ida->img.i.height);
+ if (ida->img.i.dpi)
+ len += sprintf(title+len," | %d dpi",
+ ida->img.i.dpi);
+ if (ida->img.i.npages > 1)
+ len += sprintf(title+len," | page %d/%d",
+ cpage+1, ida->img.i.npages);
+ len += sprintf(title+len," | %d%%)", viewer_i2s(ida->zoom,100));
+ XtVaSetValues(app_shell, XtNtitle,title,
+ /* XtNx,x, XtNy,y, */ XtNwidth,w, XtNheight,h,
+ NULL);
+ str = XmStringGenerate(title,NULL,XmMULTIBYTE_TEXT,NULL);
+ XtVaSetValues(status,XmNlabelString,str,NULL);
+ XmStringFree(str);
+ free(title);
+}
+
+static int
+load_file(int nr, int np)
+{
+ if(nr < 0 || nr >= nfiles)
+ return -1;
+ npages = viewer_loadimage(ida,files[nr],np);
+ if (-1 == npages)
+ return -1;
+ resize_shell();
+#if 0
+ XmListSelectPos(wlist,nr+1,False);
+ cfile = nr;
+#endif
+ return npages;
+}
+
+char*
+load_tmpfile(char *base)
+{
+ char *tmpdir;
+ char *filename;
+
+ tmpdir = getenv("TMPDIR");
+ if (NULL == tmpdir)
+ tmpdir="/tmp";
+ filename = malloc(strlen(tmpdir)+strlen(base)+16);
+ sprintf(filename,"%s/%s-XXXXXX",tmpdir,base);
+ return filename;
+}
+
+static void
+load_logo(void)
+{
+ static unsigned char logo[] = {
+#include "logo.h"
+ };
+ char *filename = load_tmpfile("ida-logo");
+ int fd;
+ fd = mkstemp(filename);
+ write(fd,logo,sizeof(logo));
+ close(fd);
+ cpage = 0;
+ npages = 1;
+ if (0 < viewer_loadimage(ida,filename,cpage)) {
+ ida->file = "ida " VERSION;
+ resize_shell();
+ }
+ unlink(filename);
+ free(filename);
+}
+
+static void
+load_stdin(void)
+{
+ char *filename = load_tmpfile("ida-stdin");
+ char buf[4096];
+ int rc,fd;
+ fd = mkstemp(filename);
+ for (;;) {
+ rc = read(0,buf,sizeof(buf));
+ if (rc <= 0)
+ break;
+ write(fd,buf,rc);
+ }
+ close(fd);
+ cpage = 0;
+ npages = 1;
+ if (0 < viewer_loadimage(ida,filename,cpage)) {
+ ida->file = "stdin";
+ resize_shell();
+ }
+ unlink(filename);
+ free(filename);
+}
+
+void
+next_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ for (;;) {
+ if (cfile >= nfiles-1)
+ return;
+ cfile++;
+ cpage = 0;
+ if (0 <= load_file(cfile,cpage))
+ break;
+ }
+}
+
+void
+prev_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ for (;;) {
+ if (cfile < 1)
+ return;
+ cfile--;
+ cpage = 0;
+ if (0 <= load_file(cfile,cpage))
+ break;
+ }
+}
+
+void
+next_page_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ for (;;) {
+ if (cpage >= npages-1)
+ return;
+ cpage++;
+ if (0 <= load_file(cfile,cpage))
+ break;
+ }
+}
+
+void
+prev_page_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ for (;;) {
+ if (cpage <= 0)
+ return;
+ cpage--;
+ if (0 <= load_file(cfile,cpage))
+ break;
+ }
+}
+
+void
+zoom_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ int zoom;
+
+ if (0 == *num)
+ return;
+
+ if (0 == strcasecmp(params[0],"auto")) {
+ viewer_autozoom(ida);
+ return;
+ }
+
+ if (0 == strcasecmp(params[0],"inc")) {
+ zoom = ida->zoom+1;
+ } else if (0 == strcasecmp(params[0],"dec")) {
+ zoom = ida->zoom-1;
+ } else {
+ zoom = atoi(params[0]);
+ }
+ viewer_setzoom(ida,zoom);
+ resize_shell();
+}
+
+void
+scroll_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ fprintf(stderr,"Scroll(): %s\n",XtName(widget));
+}
+
+/* ---------------------------------------------------------------------- */
+
+void new_file(char *name, int complain)
+{
+ struct stat st;
+ int n;
+
+ if (curl_is_url(name))
+ goto load;
+
+ if (0 == strncasecmp(name,"file:",5))
+ name += 5;
+ if (-1 == stat(name,&st)) {
+ if (complain)
+ fprintf(stderr,"stat %s: %s\n",name,strerror(errno));
+ return;
+ }
+ switch (st.st_mode & S_IFMT) {
+ case S_IFDIR:
+ browser_window(name);
+ break;
+ case S_IFREG:
+ goto load;
+ break;
+ }
+ return;
+
+ load:
+ n = list_append(name);
+ list_update();
+ cpage = 0;
+ load_file(n,cpage);
+}
+
+static void
+load_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ XmFileSelectionBoxCallbackStruct *cb = call_data;
+ char *line;
+
+ if (cb->reason == XmCR_OK) {
+ line = XmStringUnparse(cb->value,NULL,
+ XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
+ NULL,0,0);
+ new_file(line,1);
+ }
+ XtUnmanageChild(widget);
+}
+
+void
+load_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ Widget help;
+
+ if (NULL == loadbox) {
+ loadbox = XmCreateFileSelectionDialog(app_shell,"load",NULL,0);
+ help = XmFileSelectionBoxGetChild(loadbox,XmDIALOG_HELP_BUTTON);
+ XtUnmanageChild(help);
+ XtAddCallback(loadbox,XmNokCallback,load_done_cb,NULL);
+ XtAddCallback(loadbox,XmNcancelCallback,load_done_cb,NULL);
+ } else {
+ XmFileSelectionDoSearch(loadbox,NULL);
+ }
+ XtManageChild(loadbox);
+}
+
+void
+scan_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+#ifdef HAVE_LIBSANE
+ cpage = 0;
+ if (*num)
+ npages = viewer_loader_start(ida, &sane_loader, NULL, params[0], 0);
+ else
+ npages = viewer_loader_start(ida, &sane_loader, NULL, "", 0);
+ if (-1 == npages)
+ return;
+ ida->file = "scanned image";
+ resize_shell();
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void
+do_save_print(void)
+{
+ FILE *fp;
+
+ if (save_filename) {
+ XtUnmanageChild(savebox);
+ ptr_busy();
+ if (NULL == (fp = fopen(save_filename,"wb"))) {
+ fprintf(stderr,"save: can't open %s: %s\n",
+ save_filename,strerror(errno));
+ } else if (-1 == cwriter->write(fp,&ida->img)) {
+ fclose(fp);
+ fprintf(stderr,"saving %s FAILED",save_filename);
+ } else {
+ fclose(fp);
+ list_append(save_filename);
+ list_update();
+ }
+ ptr_idle();
+ }
+ if (print_command) {
+ XtUnmanageChild(printbox);
+ ptr_busy();
+ if (NULL == (fp = popen(print_command,"w"))) {
+ fprintf(stderr,"print: can't exec %s: %s\n",
+ print_command,strerror(errno));
+ } else {
+ if (-1 == cwriter->write(fp,&ida->img))
+ fprintf(stderr,"printing FAILED");
+ fclose(fp);
+ }
+ ptr_idle();
+ }
+}
+
+static void
+save_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ XmFileSelectionBoxCallbackStruct *cb = call_data;
+
+ if (cb->reason == XmCR_OK) {
+ print_command = NULL;
+ save_filename = XmStringUnparse(cb->value,NULL,
+ XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
+ NULL,0,0);
+ if (cwriter->conf) {
+ cwriter->conf(widget,&ida->img);
+ } else {
+ do_save_print();
+ }
+ } else {
+ XtUnmanageChild(widget);
+ }
+}
+
+static void
+save_fmt_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ cwriter = clientdata;
+}
+
+static void
+save_ext_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ Widget option = clientdata;
+ Widget menu;
+ WidgetList children;
+ Cardinal nchildren;
+ struct ida_writer *wr = NULL;
+ struct list_head *item;
+ char *name,*ext;
+ int i,j,pick;
+
+ name = XmTextGetString(widget);
+ ext = strrchr(name,'.');
+ if (NULL == ext)
+ return;
+ if (strchr(ext,'/'))
+ return;
+ ext++;
+
+ i = 0; pick = -1;
+ list_for_each(item,&writers) {
+ wr = list_entry(item, struct ida_writer, list);
+ for (j = 0; NULL != wr->ext[j]; j++)
+ if (0 == strcasecmp(ext,wr->ext[j]))
+ pick = i;
+ if (-1 != pick)
+ break;
+ i++;
+ }
+ if (-1 == pick)
+ return;
+
+ XtVaGetValues(option,XmNsubMenuId,&menu,NULL);
+ XtVaGetValues(menu,XtNchildren,&children,
+ XtNnumChildren,&nchildren,NULL);
+ XtVaSetValues(option,XmNmenuHistory,children[pick],NULL);
+ cwriter = wr;
+}
+
+static void
+save_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ Widget help,menu,option,push,text;
+ Arg args[2];
+ struct ida_writer *wr = NULL;
+ struct list_head *item;
+
+ if (NULL == savebox) {
+ savebox = XmCreateFileSelectionDialog(app_shell,"save",NULL,0);
+ help = XmFileSelectionBoxGetChild(savebox,XmDIALOG_HELP_BUTTON);
+ text = XmFileSelectionBoxGetChild(savebox,XmDIALOG_TEXT);
+ XtUnmanageChild(help);
+
+ menu = XmCreatePulldownMenu(savebox,"formatM",NULL,0);
+ XtSetArg(args[0],XmNsubMenuId,menu);
+ option = XmCreateOptionMenu(savebox,"format",args,1);
+ XtManageChild(option);
+ list_for_each(item,&writers) {
+ wr = list_entry(item, struct ida_writer, list);
+ push = XtVaCreateManagedWidget(wr->label,
+ xmPushButtonWidgetClass,menu,
+ NULL);
+ XtAddCallback(push,XmNactivateCallback,save_fmt_cb,wr);
+ }
+ cwriter = list_entry(writers.next, struct ida_writer, list);
+
+ XtAddCallback(text,XmNvalueChangedCallback,save_ext_cb,option);
+ XtAddCallback(savebox,XmNokCallback,save_done_cb,NULL);
+ XtAddCallback(savebox,XmNcancelCallback,save_done_cb,NULL);
+ } else {
+ XmFileSelectionDoSearch(savebox,NULL);
+ }
+ XtManageChild(savebox);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+print_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ XmSelectionBoxCallbackStruct *cb = call_data;
+
+ if (cb->reason == XmCR_OK) {
+ save_filename = NULL;
+ print_command = XmStringUnparse(cb->value,NULL,
+ XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
+ NULL,0,0);
+ cwriter = &ps_writer;
+ cwriter->conf(widget,&ida->img);
+ } else {
+ XtUnmanageChild(widget);
+ }
+}
+
+static void
+print_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ if (NULL == printbox) {
+ printbox = XmCreatePromptDialog(app_shell,"print",NULL,0);
+ XtUnmanageChild(XmSelectionBoxGetChild(printbox,XmDIALOG_HELP_BUTTON));
+ XtAddCallback(printbox,XmNokCallback,print_done_cb,NULL);
+ XtAddCallback(printbox,XmNcancelCallback,print_done_cb,NULL);
+ }
+ XtManageChild(printbox);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static struct ida_op *ops[] = {
+ &desc_flip_vert,
+ &desc_flip_horz,
+ &desc_rotate_cw,
+ &desc_rotate_ccw,
+ &desc_invert,
+ &desc_crop,
+ &desc_autocrop,
+ &desc_grayscale,
+ NULL
+};
+
+void
+filter_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ struct ida_op *op = NULL;
+ int i;
+
+ if (*num < 1)
+ return;
+ for (i = 0; NULL != ops[i]; i++) {
+ op = ops[i];
+ if (0 == strcasecmp(op->name,params[0]))
+ break;
+ }
+ if (NULL == ops[i]) {
+ fprintf(stderr,"Oops: unknown filter: %s\n",params[0]);
+ return;
+ }
+
+ viewer_start_op(ida,op,NULL);
+ if (ida->op_src.i.width != ida->img.i.width ||
+ ida->op_src.i.height != ida->img.i.height)
+ resize_shell();
+}
+
+void
+f3x3_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ struct op_3x3_parm p;
+
+ if (*num < 9) {
+ fprintf(stderr,"F3x3: wrong number of args (%d, need 9)\n",*num);
+ return;
+ }
+ memset(&p,0,sizeof(p));
+ p.f1[0] = atoi(params[0]);
+ p.f1[1] = atoi(params[1]);
+ p.f1[2] = atoi(params[2]);
+ p.f2[0] = atoi(params[3]);
+ p.f2[1] = atoi(params[4]);
+ p.f2[2] = atoi(params[5]);
+ p.f3[0] = atoi(params[6]);
+ p.f3[1] = atoi(params[7]);
+ p.f3[2] = atoi(params[8]);
+ if (*num > 9) p.mul = atoi(params[ 9]);
+ if (*num > 10) p.div = atoi(params[10]);
+ if (*num > 11) p.add = atoi(params[11]);
+ if (debug) {
+ fprintf(stderr,"f3x3: -----------\n");
+ fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f1[0],p.f1[1],p.f1[2]);
+ fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f2[0],p.f2[1],p.f2[2]);
+ fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f3[0],p.f3[1],p.f3[2]);
+ fprintf(stderr,"f3x3: *%d/%d+%d\n",p.mul,p.div,p.add);
+ }
+ viewer_start_op(ida,&desc_3x3,&p);
+}
+
+void
+undo_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ Widget msgbox;
+ int resize;
+
+ resize = (ida->undo.i.width != ida->img.i.width ||
+ ida->undo.i.height != ida->img.i.height);
+ if (-1 == viewer_undo(ida)) {
+ msgbox = XmCreateInformationDialog(app_shell,"noundobox",NULL,0);
+ XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON));
+ XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON));
+ XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox);
+ XtManageChild(msgbox);
+ } else {
+ if (resize)
+ resize_shell();
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct ida_prompt {
+ Widget shell;
+ Widget box;
+ Widget scale;
+ Widget text;
+ int apply;
+ int value;
+ int decimal;
+ int factor; /* 10^decimal */
+ void (*notify)(int value, int preview);
+};
+
+static void
+prompt_setvalue(struct ida_prompt *me, int value, int scale, int text)
+{
+ char str[32];
+ int min,max;
+
+ if (me->value == value)
+ return;
+ XtVaGetValues(me->scale,XmNminimum,&min,XmNmaximum,&max,NULL);
+ if (value < min || value > max)
+ return;
+
+ me->value = value;
+ if (scale)
+ XmScaleSetValue(me->scale,value);
+ if (text) {
+ if (me->decimal) {
+ sprintf(str,"%*.*f",me->decimal+2,me->decimal,
+ (float)value/me->factor);
+ } else {
+ sprintf(str,"%d",value);
+ }
+ XmTextSetString(me->text,str);
+ }
+ if (me->notify)
+ me->notify(value,1);
+}
+
+static void
+prompt_scale_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_prompt *me = client_data;
+ XmScaleCallbackStruct *cd = calldata;
+
+ prompt_setvalue(me,cd->value,0,1);
+}
+
+static void
+prompt_text_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_prompt *me = client_data;
+ float fvalue;
+ int value;
+
+ if (me->decimal) {
+ fvalue = atof(XmTextGetString(me->text));
+ fvalue += 0.5/me->factor;
+ value = (int)(fvalue * me->factor);
+ } else {
+ value = atoi(XmTextGetString(me->text));
+ }
+ prompt_setvalue(me,value,1,0);
+}
+
+static void
+prompt_box_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_prompt *me = client_data;
+ XmSelectionBoxCallbackStruct *cd = calldata;
+
+ if (XmCR_OK == cd->reason)
+ me->apply = 1;
+ XtDestroyWidget(me->shell);
+}
+
+static void
+prompt_shell_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_prompt *me = client_data;
+
+ if (me->apply)
+ me->notify(me->value,0);
+ else
+ viewer_cancel_preview(ida);
+ free(me);
+}
+
+static void
+prompt_init(char *name, int decimal, int value,
+ void (*notify)(int value, int preview))
+{
+ struct ida_prompt *me;
+
+ me = malloc(sizeof(*me));
+ memset(me,0,sizeof(*me));
+ if (decimal) {
+ int i;
+ me->decimal = decimal;
+ me->factor = 1;
+ for (i = 0; i < decimal; i++)
+ me->factor *= 10;
+ }
+ me->notify = notify;
+
+ me->box = XmCreatePromptDialog(app_shell,name,NULL,0);
+ me->shell = XtParent(me->box);
+ me->text = XmSelectionBoxGetChild(me->box,XmDIALOG_TEXT);
+ XmdRegisterEditres(XtParent(me->box));
+ XtUnmanageChild(XmSelectionBoxGetChild(me->box,XmDIALOG_HELP_BUTTON));
+ me->scale = XtVaCreateManagedWidget("scale",xmScaleWidgetClass,
+ me->box,NULL);
+
+ XtAddCallback(me->scale,XmNdragCallback,prompt_scale_cb,me);
+ XtAddCallback(me->scale,XmNvalueChangedCallback,prompt_scale_cb,me);
+ XtAddCallback(me->text,XmNvalueChangedCallback,prompt_text_cb,me);
+ XtAddCallback(me->box,XmNokCallback,prompt_box_cb,me);
+ XtAddCallback(me->box,XmNcancelCallback,prompt_box_cb,me);
+ XtAddCallback(me->shell,XmNdestroyCallback,prompt_shell_cb,me);
+
+ XtManageChild(me->box);
+ prompt_setvalue(me,value,1,1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+gamma_notify(int value, int preview)
+{
+ struct op_map_parm param;
+ float gamma = (float)value/100;
+
+ param.red = op_map_nothing;
+ param.red.gamma = gamma;
+ param.green = param.red;
+ param.blue = param.red;
+ if (preview) {
+ viewer_start_preview(ida,&desc_map,&param);
+ } else {
+ gamma_val = value;
+ viewer_start_op(ida,&desc_map,&param);
+ }
+}
+
+void
+gamma_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ prompt_init("gamma",2,gamma_val,gamma_notify);
+}
+
+static void
+bright_notify(int value, int preview)
+{
+ struct op_map_parm param;
+
+ param.red = op_map_nothing;
+ param.red.bottom += value;
+ param.red.top += value;
+ param.green = param.red;
+ param.blue = param.red;
+ if (preview) {
+ viewer_start_preview(ida,&desc_map,&param);
+ } else {
+ bright_val = value;
+ viewer_start_op(ida,&desc_map,&param);
+ }
+}
+
+void
+bright_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ prompt_init("bright",0,bright_val,bright_notify);
+}
+
+static void
+contrast_notify(int value, int preview)
+{
+ struct op_map_parm param;
+
+ param.red = op_map_nothing;
+ param.red.bottom -= value;
+ param.red.top += value;
+ param.green = param.red;
+ param.blue = param.red;
+ if (preview) {
+ viewer_start_preview(ida,&desc_map,&param);
+ } else {
+ contrast_val = value;
+ viewer_start_op(ida,&desc_map,&param);
+ }
+}
+
+void
+contrast_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ prompt_init("contrast",0,contrast_val,contrast_notify);
+}
+
+static void
+rotate_notify(int value, int preview)
+{
+ struct op_rotate_parm parm;
+
+ parm.angle = value;
+ if (preview) {
+ viewer_start_preview(ida,&desc_rotate,&parm);
+ } else {
+ rotate_val = value;
+ viewer_start_op(ida,&desc_rotate,&parm);
+ }
+}
+
+void
+rotate_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ prompt_init("rotate",0,rotate_val,rotate_notify);
+}
+
+static void
+sharpe_notify(int value, int preview)
+{
+ struct op_sharpe_parm parm;
+
+ parm.factor = value;
+ if (preview) {
+ viewer_start_preview(ida,&desc_sharpe,&parm);
+ } else {
+ sharpe_val = value;
+ viewer_start_op(ida,&desc_sharpe,&parm);
+ }
+}
+
+void
+sharpe_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ prompt_init("sharpe",0,sharpe_val,sharpe_notify);
+}
+
+void
+color_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ color_init(&ida->img);
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct ida_resize {
+ Widget dlg,tx,ty,tr,lock,size,res,label;
+ int yupdate,xupdate,rupdate;
+ int apply;
+};
+
+static void
+resize_phys_size(struct ida_resize *h)
+{
+ char buf[128];
+ XmString str;
+ int dpi;
+ float x,y;
+
+ dpi = atoi(XmTextGetString(h->tr));
+ if (dpi) {
+ x = (float)atoi(XmTextGetString(h->tx)) / dpi;
+ y = (float)atoi(XmTextGetString(h->ty)) / dpi;
+ sprintf(buf,"%.2f x %.2f inch\n%.2f x %.2f cm",
+ x,y, x*2.54, y*2.54);
+ } else {
+ strcpy(buf,"unknown");
+ }
+ str = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL);
+ XtVaSetValues(h->label,XmNlabelString,str,NULL);
+}
+
+static void
+resize_sync_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_resize *h = client_data;
+ char buf[32];
+ int i,lock,res;
+
+ lock = XmToggleButtonGetState(h->lock);
+ res = XmToggleButtonGetState(h->res);
+
+ /* update text fields */
+ if (h->tx == widget) {
+ if (h->xupdate) {
+ h->xupdate--;
+ return;
+ }
+ i = atoi(XmTextGetString(h->tx));
+ if (lock) {
+ sprintf(buf,"%d",i * ida->img.i.height / ida->img.i.width);
+ h->yupdate++;
+ XmTextSetString(h->ty,buf);
+ if (res) {
+ sprintf(buf,"%d", ida->img.i.dpi * i / ida->img.i.width);
+ h->rupdate++;
+ XmTextSetString(h->tr,buf);
+ }
+ } else {
+ if (res) {
+ h->rupdate++;
+ XmTextSetString(h->tr,"0");
+ }
+ }
+ resize_phys_size(h);
+ }
+ if (h->ty == widget) {
+ if (h->yupdate) {
+ h->yupdate--;
+ return;
+ }
+ i = atoi(XmTextGetString(h->ty));
+ if (lock) {
+ sprintf(buf,"%d",i * ida->img.i.width / ida->img.i.height);
+ h->xupdate++;
+ XmTextSetString(h->tx,buf);
+ if (res) {
+ sprintf(buf,"%d", ida->img.i.dpi * i / ida->img.i.height);
+ h->rupdate++;
+ XmTextSetString(h->tr,buf);
+ }
+ } else {
+ if (res) {
+ h->rupdate++;
+ XmTextSetString(h->tr,"0");
+ }
+ }
+ resize_phys_size(h);
+ }
+ if (h->tr == widget) {
+ if (h->rupdate) {
+ h->rupdate--;
+ return;
+ }
+ i = atoi(XmTextGetString(h->tr));
+ sprintf(buf,"%d", ida->img.i.width * i / ida->img.i.dpi);
+ h->xupdate++;
+ XmTextSetString(h->tx,buf);
+ sprintf(buf,"%d", ida->img.i.height * i / ida->img.i.dpi);
+ h->yupdate++;
+ XmTextSetString(h->ty,buf);
+ resize_phys_size(h);
+ }
+
+ /* radio buttons pressed */
+ if (h->size == widget && XmToggleButtonGetState(h->size)) {
+ XmToggleButtonSetState(h->res,0,False);
+ sprintf(buf,"%d", ida->img.i.dpi);
+ h->rupdate++;
+ XmTextSetString(h->tr,buf);
+ XtVaSetValues(h->tr,XmNsensitive,False,NULL);
+ resize_phys_size(h);
+ }
+ if (h->res == widget && XmToggleButtonGetState(h->res)) {
+ XmToggleButtonSetState(h->size,0,False);
+ XtVaSetValues(h->tr,XmNsensitive,True,NULL);
+ }
+}
+
+static void
+resize_button_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_resize *h = client_data;
+ XmSelectionBoxCallbackStruct *cb = calldata;
+
+ if (cb->reason == XmCR_OK)
+ h->apply = 1;
+ XtDestroyWidget(XtParent(h->dlg));
+}
+
+static void
+resize_destroy(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_resize *h = client_data;
+ struct op_resize_parm param;
+
+ if (!h->apply)
+ return;
+ param.width = atoi(XmTextGetString(h->tx));
+ param.height = atoi(XmTextGetString(h->ty));
+ param.dpi = atoi(XmTextGetString(h->tr));
+ if (0 == param.width ||
+ 0 == param.height) {
+ fprintf(stderr,"resize: invalid argument\n");
+ return;
+ }
+
+ viewer_start_op(ida,&desc_resize,&param);
+ resize_shell();
+ free(h);
+}
+
+static void
+resize_ac(Widget widget, XEvent *event, String *params, Cardinal *num)
+{
+ Widget rc,rc2;
+ char buf[32];
+ struct ida_resize *h;
+
+ h = malloc(sizeof(*h));
+ memset(h,0,sizeof(*h));
+
+ h->dlg = XmCreatePromptDialog(app_shell,"resize",NULL,0);
+ XmdRegisterEditres(XtParent(h->dlg));
+ XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_SELECTION_LABEL));
+ XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_HELP_BUTTON));
+ XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_TEXT));
+ rc = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass,h->dlg, NULL);
+ XtVaCreateManagedWidget("lx", xmLabelWidgetClass,rc, NULL);
+ h->tx = XtVaCreateManagedWidget("tx", xmTextWidgetClass,rc, NULL);
+ XtVaCreateManagedWidget("ly", xmLabelWidgetClass,rc, NULL);
+ h->ty = XtVaCreateManagedWidget("ty", xmTextWidgetClass,rc, NULL);
+ XtVaCreateManagedWidget("lr", xmLabelWidgetClass,rc, NULL);
+ h->tr = XtVaCreateManagedWidget("tr", xmTextWidgetClass,rc, NULL);
+ h->lock = XtVaCreateManagedWidget("lock", xmToggleButtonWidgetClass,
+ rc, NULL);
+ rc2 = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass,rc, NULL);
+ h->size = XtVaCreateManagedWidget("size", xmToggleButtonWidgetClass,
+ rc2, NULL);
+ h->res = XtVaCreateManagedWidget("res", xmToggleButtonWidgetClass,
+ rc2, NULL);
+ XtVaCreateManagedWidget("phys", xmLabelWidgetClass,rc,NULL);
+ h->label = XtVaCreateManagedWidget("label", xmLabelWidgetClass,
+ rc, NULL);
+
+ sprintf(buf,"%d",ida->img.i.width);
+ XmTextSetString(h->tx,buf);
+ sprintf(buf,"%d",ida->img.i.height);
+ XmTextSetString(h->ty,buf);
+ sprintf(buf,"%d",ida->img.i.dpi);
+ XmTextSetString(h->tr,buf);
+ XtVaSetValues(h->tr,XmNsensitive,False,NULL);
+ XmToggleButtonSetState(h->lock,1,False);
+ XmToggleButtonSetState(h->size,1,False);
+ XmToggleButtonSetState(h->res,0,False);
+ if (!ida->img.i.dpi) {
+ XtVaSetValues(h->size,XmNsensitive,False,NULL);
+ XtVaSetValues(h->res, XmNsensitive,False,NULL);
+ }
+ resize_phys_size(h);
+
+ XtAddCallback(XtParent(h->dlg),XmNdestroyCallback,resize_destroy,h);
+ XtAddCallback(h->dlg, XmNokCallback, resize_button_cb, h);
+ XtAddCallback(h->dlg, XmNcancelCallback, resize_button_cb, h);
+ XtAddCallback(h->tx, XmNvalueChangedCallback, resize_sync_cb, h);
+ XtAddCallback(h->ty, XmNvalueChangedCallback, resize_sync_cb, h);
+ XtAddCallback(h->tr, XmNvalueChangedCallback, resize_sync_cb, h);
+ XtAddCallback(h->size,XmNvalueChangedCallback, resize_sync_cb, h);
+ XtAddCallback(h->res, XmNvalueChangedCallback, resize_sync_cb, h);
+ XtManageChild(h->dlg);
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct stderr_handler {
+ Widget box;
+ XmString str;
+ int pipe,err;
+ XtInputId id;
+};
+
+static void
+stderr_input(XtPointer clientdata, int *src, XtInputId *id)
+{
+ struct stderr_handler *h = clientdata;
+ XmString item;
+ Widget label;
+ char buf[1024];
+ int rc;
+
+ rc = read(h->pipe,buf,sizeof(buf)-1);
+ if (rc <= 0) {
+ /* Oops */
+ XtRemoveInput(h->id);
+ close(h->pipe);
+ XtDestroyWidget(h->box);
+ free(h);
+ }
+ buf[rc] = 0;
+ write(h->err,buf,rc);
+ item = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL);
+ h->str = XmStringConcatAndFree(h->str,item);
+ label = XmMessageBoxGetChild(h->box,XmDIALOG_MESSAGE_LABEL);
+ XtVaSetValues(label,XmNlabelString,h->str,NULL);
+ XtManageChild(h->box);
+}
+
+static void
+stderr_ok_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ struct stderr_handler *h = clientdata;
+
+ XmStringFree(h->str);
+ h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL);
+ XtUnmanageChild(h->box);
+}
+
+static void
+stderr_close_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
+{
+ struct stderr_handler *h = clientdata;
+
+ XmStringFree(h->str);
+ h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL);
+}
+
+static void
+stderr_init(void)
+{
+ struct stderr_handler *h;
+ int p[2];
+
+ h = malloc(sizeof(*h));
+ memset(h,0,sizeof(*h));
+ h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL);
+ h->box = XmCreateErrorDialog(app_shell,"errbox",NULL,0);
+ XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_HELP_BUTTON));
+ XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_CANCEL_BUTTON));
+ XtAddCallback(h->box,XmNokCallback,stderr_ok_cb,h);
+ XtAddCallback(XtParent(h->box),XmNpopdownCallback,stderr_close_cb,h);
+ XSync(XtDisplay(app_shell),False);
+ if (!debug) {
+ pipe(p);
+ h->err = dup(2);
+ dup2(p[1],2);
+ close(p[1]);
+ h->pipe = p[0];
+ h->id = XtAppAddInput(app_context,h->pipe,(XtPointer)XtInputReadMask,
+ stderr_input,h);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+create_mainwindow(void)
+{
+ Widget img;
+
+ XmdRegisterEditres(app_shell);
+ view = XmCreateScrolledWindow(app_shell,"view",NULL,0);
+ XtManageChild(view);
+ img = XtVaCreateManagedWidget("image", xmDrawingAreaWidgetClass,view,NULL);
+ XtAddCallback(img,XmNdestinationCallback,selection_dest,NULL);
+ XtAddCallback(img,XmNconvertCallback,selection_convert,NULL);
+ dnd_add(img);
+ ida = viewer_init(img);
+ XtInstallAllAccelerators(img,app_shell);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "ida " VERSION " - image viewer & editor\n"
+ "usage: ida [ options ] [ files ]\n"
+ "options:\n"
+ " -h, -help this text\n"
+ " -pcd n pick PhotoCD size (n = 1 .. 5, default 3)\n"
+ " -d, -debug enable debug messages\n");
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, files, zero = 0;
+ struct stat st;
+ Pixel background;
+
+ setlocale(LC_ALL,"");
+ if (0 == strcasecmp("utf-8", nl_langinfo(CODESET))) {
+ /* ### FIXME ###
+ * for not-yet known reasons ida crashes somewhere deep in
+ * the Motif libraries when running in utf-8 locale ... */
+ setenv("LC_ALL", "POSIX", 1);
+ setlocale(LC_ALL,"");
+ }
+
+ binary = argv[0];
+ ida_init_config();
+ ida_read_config();
+
+ XtSetLanguageProc(NULL,NULL,NULL);
+ app_shell = XtAppInitialize(&app_context, "Ida",
+ opt_desc, opt_count,
+ &argc, argv,
+ fallback_ressources,
+ NULL, 0);
+ dpy = XtDisplay(app_shell);
+ XtGetApplicationResources(app_shell,&args,
+ args_desc,args_count,
+ NULL,0);
+ pcd_res = GET_PHOTOCD_RES();
+ sane_res = GET_SANE_RES();
+ if (args.help)
+ usage();
+ if (args.debug) {
+ debug=1;
+ xdnd_debug = 1;
+ XSynchronize(dpy,1);
+ }
+
+ XtAppAddActions(app_context, actionTable,
+ sizeof(actionTable) / sizeof(XtActionsRec));
+ if (0) {
+ XtAddCallback(XmGetXmDisplay(dpy),XmNnoFontCallback,
+ display_cb,NULL);
+ XtAddCallback(XmGetXmDisplay(dpy),XmNnoRenditionCallback,
+ display_cb,NULL);
+ }
+ XtVaGetValues(app_shell, XtNbackground,&background, NULL);
+ x11_color_init(app_shell,&gray);
+ x11_icons_init(dpy, background /* x11_gray */);
+ stderr_init();
+ ipc_init();
+
+ wm_delete_window = XInternAtom(dpy,"WM_DELETE_WINDOW",False);
+ create_mainwindow();
+ create_control();
+ XtRealizeWidget(app_shell);
+ ptr_register(ida->widget);
+ ptr_register(control_shell);
+
+ /* handle cmd line args */
+ if (2 == argc && 0 == strcmp(argv[1],"-")) {
+ load_stdin();
+ } else if (argc > 1) {
+ for (files = 0, i = 1; i < argc; i++) {
+ if (curl_is_url(argv[i])) {
+ list_append(argv[i]);
+ files++;
+ continue;
+ }
+ if (-1 == stat(argv[i],&st)) {
+ if (debug)
+ fprintf(stderr,"stat %s: %s\n",argv[i],strerror(errno));
+ continue;
+ }
+ switch (st.st_mode & S_IFMT) {
+ case S_IFDIR:
+ browser_window(argv[i]);
+ break;
+ case S_IFREG:
+ list_append(argv[i]);
+ files++;
+ break;
+ }
+ }
+ if (files) {
+ list_update();
+ next_ac(ida->widget,NULL,NULL,&zero);
+ }
+ }
+
+ if (NULL == ida->file)
+ load_logo();
+
+ XtAppMainLoop(app_context);
+ return 0; /* keep compiler happy */
+}