/* editor.c -- create a full-blown Motif editor application complete ** with a menubar, facilities to read and write files, text search ** and replace, clipboard support and so forth. */ #include #include #include #include #include #include #include #include #include #include #include #include Widget text_edit, search_text, replace_text, text_output; #define FILE_OPEN 0 #define FILE_SAVE 1 #define FILE_EXIT 2 #define EDIT_CUT 0 #define EDIT_COPY 1 #define EDIT_PASTE 2 #define EDIT_CLEAR 3 #define SEARCH_FIND_NEXT 0 #define SEARCH_SHOW_ALL 1 #define SEARCH_REPLACE 2 #define SEARCH_CLEAR 3 main (int argc, char *argv[]) { XtAppContext app_context; Widget toplevel, main_window, menubar, form, search_panel, label_w; void file_cb(Widget, XtPointer, XtPointer); void edit_cb(Widget, XtPointer, XtPointer); void search_cb(Widget, XtPointer, XtPointer); Arg args[10]; int n = 0; XmString open, save, exit, exit_acc, file, edit, cut, clear, copy, paste, search, next, find, replace; XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaOpenApplication (&app_context, "Xeditor", NULL, 0, &argc, argv, NULL, sessionShellWidgetClass, NULL); main_window = XmCreateMainWindow (toplevel, "main_window", NULL, 0); /* Create a simple MenuBar that contains three menus */ file = XmStringCreateLocalized ("File"); edit = XmStringCreateLocalized ("Edit"); search = XmStringCreateLocalized ("Search"); menubar = XmVaCreateSimpleMenuBar (main_window, "menubar", XmVaCASCADEBUTTON, file, 'F', XmVaCASCADEBUTTON, edit, 'E', XmVaCASCADEBUTTON, search, 'S', NULL); XmStringFree (file); XmStringFree (edit); XmStringFree (search); /* First menu is the File menu -- callback is file_cb() */ open = XmStringCreateLocalized ("Open..."); save = XmStringCreateLocalized ("Save..."); exit = XmStringCreateLocalized ("Exit"); exit_acc = XmStringCreateLocalized ("Ctrl+C"); XmVaCreateSimplePulldownMenu (menubar, "file_menu", 0, file_cb, XmVaPUSHBUTTON, open, 'O', NULL, NULL, XmVaPUSHBUTTON, save, 'S', NULL, NULL, XmVaSEPARATOR, XmVaPUSHBUTTON, exit, 'x', "Ctrlc", exit_acc, NULL); XmStringFree (open); XmStringFree (save); XmStringFree (exit); XmStringFree (exit_acc); /* ...create the "Edit" menu -- callback is edit_cb() */ cut = XmStringCreateLocalized ("Cut"); copy = XmStringCreateLocalized ("Copy"); clear = XmStringCreateLocalized ("Clear"); paste = XmStringCreateLocalized ("Paste"); XmVaCreateSimplePulldownMenu (menubar, "edit_menu", 1, edit_cb, XmVaPUSHBUTTON, cut, 't', NULL, NULL, XmVaPUSHBUTTON, copy, 'C', NULL, NULL, XmVaPUSHBUTTON, paste, 'P', NULL, NULL, XmVaSEPARATOR, XmVaPUSHBUTTON, clear, 'l', NULL, NULL, NULL); XmStringFree (cut); XmStringFree (copy); XmStringFree (paste); /* create the "Search" menu -- callback is search_cb() */ next = XmStringCreateLocalized ("Find Next"); find = XmStringCreateLocalized ("Show All"); replace = XmStringCreateLocalized ("Replace Text"); XmVaCreateSimplePulldownMenu (menubar, "search_menu", 2, search_cb, XmVaPUSHBUTTON, next, 'N', NULL, NULL, XmVaPUSHBUTTON, find, 'A', NULL, NULL, XmVaPUSHBUTTON, replace, 'R', NULL, NULL, XmVaSEPARATOR, XmVaPUSHBUTTON, clear, 'C', NULL, NULL, NULL); XmStringFree (next); XmStringFree (find); XmStringFree (replace); XmStringFree (clear); XtManageChild (menubar); /* create a form work are */ form = XmCreateForm (main_window, "form", NULL, 0); /* create horizontal RowColumn inside the form */ n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNpacking, XmPACK_TIGHT); n++; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; search_panel = XmCreateRowColumn (form, "search_panel", args, n); /* Create two TextField widgets with Labels... */ label_w = XmCreateLabelGadget (search_panel, "Search Pattern:", NULL, 0); XtManageChild (label_w); search_text = XmCreateTextField (search_panel, "search_text", NULL, 0); XtManageChild (search_text); label_w = XmCreateLabelGadget (search_panel, "Replace Pattern:", NULL, 0); XtManageChild (label_w); replace_text = XmCreateTextField (search_panel, "replace_text", NULL, 0); XtManageChild (replace_text); XtManageChild (search_panel); n = 0; XtSetArg (args[n], XmNeditable, False); n++; XtSetArg (args[n], XmNcursorPositionVisible, False); n++; XtSetArg (args[n], XmNshadowThickness, 0); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; text_output = XmCreateTextField (form, "text_output", args, n); XtManageChild (text_output); n = 0; XtSetArg (args[n], XmNrows, 10); n++; XtSetArg (args[n], XmNcolumns, 80); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, search_panel); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, text_output); n++; text_edit = XmCreateScrolledText (form, "text_edit", args, n); XtManageChild (text_edit); XtManageChild (form); XtManageChild (main_window); XtRealizeWidget (toplevel); XtAppMainLoop (app_context); } /* file_select_cb() -- callback routine for "OK" button in ** FileSelectionDialogs. */ void file_select_cb (Widget dialog, XtPointer client_data, XtPointer call_data) { char buf[256], *filename, *text; struct stat statb; long len; FILE *fp; int reason = (int) client_data; XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *) call_data; if (!(filename = XmStringUnparse (cbs->value, XmFONTLIST_DEFAULT_TAG, XmCHARSET_TEXT, XmCHARSET_TEXT, NULL, 0, XmOUTPUT_ALL))) return; /* must have been an internal error */ if (*filename == NULL) { XtFree (filename); XBell (XtDisplay (text_edit), 50); XmTextSetString (text_output, "Choose a file."); return; /* nothing typed */ } if (reason == FILE_SAVE) { if (!(fp = fopen (filename, "w"))) { perror (filename); (void) sprintf (buf, "Can't save to %s.", filename); XmTextSetString (text_output, buf); XtFree (filename); return; } /* saving -- get text from Text widget... */ text = XmTextGetString (text_edit); len = XmTextGetLastPosition (text_edit); /* write it to file (check for error) */ if (fwrite (text, sizeof (char), len, fp) != len) (void) strcpy (buf, "Warning: did not write entire file!"); else { /* make sure a newline terminates file */ if (text[len-1] != '\n') fputc ('\n', fp); (void) sprintf (buf, "Saved %ld bytes to %s.", len, filename); } } else { /* reason == FILE_OPEN */ /* make sure the file is a regular text file and open it */ if (stat (filename, &statb) == -1 || (statb.st_mode & S_IFMT) != S_IFREG || !(fp = fopen (filename, "r"))) { perror (filename); (void) sprintf (buf, "Can't read %s.", filename); XmTextSetString (text_output, buf); XtFree (filename); return; } /* put the contents of the file in the Text widget by ** allocating enough space for the entire file, reading the ** file into the space, and using XmTextSetString() to show ** the file. */ len = statb.st_size; if (!(text = XtMalloc ((unsigned)(len+1)))) /* +1 for NULL */ (void) sprintf (buf, "%s: XtMalloc(%ld) failed", len, filename); else { if (fread (text, sizeof (char), len, fp) != len) (void) sprintf (buf, "Warning: did not read entire file!"); else (void) sprintf (buf, "Loaded %ld bytes from %s.", len, filename); text[len] = 0; /* NULL-terminate */ XmTextSetString (text_edit, text); } } XmTextSetString (text_output, buf); /* purge output message */ /* free all allocated space. */ XtFree (text); XtFree (filename); (void) fclose (fp); XtUnmanageChild (dialog); } /* popdown_cb() -- callback routine for "Cancel" button. */ void popdown_cb (Widget w, XtPointer client_data, XtPointer call_data) { XtUnmanageChild (w); } /* file_cb() -- a menu item from the "File" pulldown menu was selected */ void file_cb (Widget w, XtPointer client_data, XtPointer call_data) { static Widget open_dialog, save_dialog; Widget dialog = NULL; XmString button, title; int reason = (int) client_data; if (reason == FILE_EXIT) exit (0); XmTextSetString (text_output, NULL); /* clear message area */ if (reason == FILE_OPEN && open_dialog) dialog = open_dialog; else if (reason == FILE_SAVE && save_dialog) dialog = save_dialog; if (dialog) { XtManageChild (dialog); /* make sure that dialog is raised to top of window stack */ XMapRaised (XtDisplay (dialog), XtWindow (XtParent (dialog))); return; } dialog = XmCreateFileSelectionDialog (text_edit, "Files", NULL, 0); XtAddCallback (dialog, XmNcancelCallback, popdown_cb, NULL); XtAddCallback (dialog, XmNokCallback, file_select_cb, (XtPointer) reason); if (reason == FILE_OPEN) { button = XmStringCreateLocalized ("Open"); title = XmStringCreateLocalized ("Open File"); open_dialog = dialog; } else { /* reason == FILE_SAVE */ button = XmStringCreateLocalized ("Save"); title = XmStringCreateLocalized ("Save File"); save_dialog = dialog; } XtVaSetValues (dialog, XmNokLabelString, button, XmNdialogTitle, title, NULL); XmStringFree (button); XmStringFree (title); XtManageChild (dialog); } /* search_cb() -- a menu item from the "Search" pulldown menu selected */ void search_cb (Widget w, XtPointer client_data, XtPointer call_data) { char *search_pat, *p, *string, *new_pat, buf[256]; XmTextPosition pos = 0; int len, nfound = 0; int search_len, pattern_len; int reason = (int) client_data; Boolean found = False; XmTextSetString (text_output, NULL); /* clear message area */ if (reason == SEARCH_CLEAR) { pos = XmTextGetLastPosition (text_edit); XmTextSetHighlight (text_edit, 0, pos, XmHIGHLIGHT_NORMAL); return; } if (!(string = XmTextGetString (text_edit)) || !*string) { XmTextSetString (text_output, "No text to search."); return; } if (!(search_pat = XmTextGetString (search_text)) || !*search_pat) { XmTextSetString (text_output, "Specify a search pattern."); XtFree (string); return; } new_pat = XmTextGetString (replace_text); search_len = strlen (search_pat); pattern_len = strlen (new_pat); if (reason == SEARCH_FIND_NEXT) { pos = XmTextGetCursorPosition (text_edit) + 1; found = XmTextFindString (text_edit, pos, search_pat, XmTEXT_FORWARD, &pos); if (!found) found = XmTextFindString (text_edit, 0, search_pat, XmTEXT_FORWARD, &pos); if (found) nfound++; } else { /* reason == SEARCH_SHOW_ALL || reason == SEARCH_REPLACE */ do { found = XmTextFindString (text_edit, pos, search_pat, XmTEXT_FORWARD, &pos); if (found) { nfound++; if (reason == SEARCH_SHOW_ALL) XmTextSetHighlight (text_edit, pos, pos + search_len, XmHIGHLIGHT_SELECTED); else XmTextReplace (text_edit, pos, pos + search_len, new_pat); pos++; } } while (found); } if (nfound == 0) XmTextSetString (text_output, "Pattern not found."); else { switch (reason) { case SEARCH_FIND_NEXT : (void) sprintf (buf, "Pattern found at position %ld.", pos); XmTextSetInsertionPosition (text_edit, pos); break; case SEARCH_SHOW_ALL : (void) sprintf (buf, "Found %d occurrences.", nfound); break; case SEARCH_REPLACE : (void) sprintf (buf, "Made %d replacements.", nfound); } XmTextSetString (text_output, buf); } XtFree (string); XtFree (search_pat); XtFree (new_pat); } /* edit_cb() -- the callback routine for the items in the edit menu */ void edit_cb (Widget widget, XtPointer client_data, XtPointer call_data) { Boolean result = True; int reason = (int) client_data; XEvent *event = ((XmPushButtonCallbackStruct *) call_data)->event; Time when; XmTextSetString (text_output, NULL); /* clear message area */ if (event != NULL && reason == EDIT_CUT || reason == EDIT_COPY || reason == EDIT_CLEAR) { switch (event->type) { case ButtonRelease : when = event->xbutton.time; break; case KeyRelease : when = event->xkey.time; break; default : when = CurrentTime; break; } } switch (reason) { case EDIT_CUT : result = XmTextCut (text_edit, when); break; case EDIT_COPY : result = XmTextCopy (text_edit, when); break; case EDIT_PASTE : result = XmTextPaste (text_edit); /* FALLTHROUGH */ case EDIT_CLEAR : XmTextClearSelection (text_edit, when); break; } if (result == False) XmTextSetString (text_output, "There is no selection."); }