X11 Work Bench Toolkit  1.0
edit_window.c
1 
2 // _ _ _ _ _ //
3 // ___ __| |(_)| |_ __ __(_) _ __ __| | ___ __ __ ___ //
4 // / _ \ / _` || || __| \ \ /\ / /| || '_ \ / _` | / _ \\ \ /\ / / / __| //
5 // | __/| (_| || || |_ \ V V / | || | | || (_| || (_) |\ V V /_| (__ //
6 // \___| \__,_||_| \__|_____\_/\_/ |_||_| |_| \__,_| \___/ \_/\_/(_)\___| //
7 // |_____| //
8 // //
9 // a window into which you can type (and edit) text //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17  all rights reserved
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  BSD-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  BSD-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <memory.h>
53 #include <string.h>
54 #include <strings.h>
55 
56 #define _EDIT_WINDOW_C_IMPLEMENTED_
57 
58 #include "window_helper.h"
59 #include "edit_window.h"
60 #include "conf_help.h"
61 #include "draw_text.h"
62 
63 
64 #if 1 // assign to 0 to disable this trace-style debugging
65 #define CALLBACK_TRACKER WBDebugPrint("TEMPORARY: %s - callback tracker\n", __FUNCTION__);
66 #else //
67 #define CALLBACK_TRACKER { }
68 #endif // 0,1
69 
70 #define EDIT_WINDOW_LINE_SPACING 4 /* 4 spaces between each line */
71 
72 
73 int FWEditWindowEvent(Window wID, XEvent *pEvent);
74 static void InternalEditWindowDestructor(WBChildFrame *pC);
75 static void InternalEditWindowDestroy(WBEditWindow *pEditWindow);
76 
77 // UI callbacks for WBChildFrameUI
78 
79 static void internal_do_char(WBChildFrame *, XClientMessageEvent *); // handler for regular WM_CHAR Client Messages (WBChildFrame *, typed-in characters)
80 static void internal_scancode(WBChildFrame *, XClientMessageEvent *); // handler for 'other scan code' WM_CHAR Client Messages (WBChildFrame *, typed-in characters)
81 static void internal_bkspace(WBChildFrame *, int iACS); // 'backspace' delete character (WBChildFrame *, backspace equivalent). 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
82 static void internal_del(WBChildFrame *, int iACS); // 'delete' char under cursor (WBChildFrame *, delete equivalent). 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
83 static void internal_tab(WBChildFrame *, int iACS); // 'tab' char, or tab navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
84 static void internal_enter(WBChildFrame *, int iACS); // 'enter' char, or 'enter' for navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
85 static void internal_uparrow(WBChildFrame *, int iACS); // 'up' arrow navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
86 static void internal_downarrow(WBChildFrame *, int iACS); // 'down' arrow navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
87 static void internal_leftarrow(WBChildFrame *, int iACS); // 'left' arrow navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
88 static void internal_rightarrow(WBChildFrame *, int iACS); // 'right' arrow navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
89 static void internal_home(WBChildFrame *, int iACS); // 'home' arrow navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
90 static void internal_end(WBChildFrame *, int iACS); // 'end' arrow navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
91 static void internal_pgup(WBChildFrame *, int iACS); // 'page up' navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
92 static void internal_pgdown(WBChildFrame *, int iACS); // 'page down' navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
93 static void internal_pgleft(WBChildFrame *, int iACS); // 'page left' navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
94 static void internal_pgright(WBChildFrame *, int iACS); // 'page right' navigation. 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
95 static void internal_help(WBChildFrame *, int iACS); // 'help' context (WBChildFrame *, F1). 'iACS' is the Alt/Ctrl/Shift flags. \sa aWM_CHAR
96 static void internal_hover_notify(WBChildFrame *, int x, int y); // 'mouse hover' notification (WBChildFrame *, x and y are pixel coords with respect to upper left corner)
97 static void internal_hover_cancel(WBChildFrame *); // 'mouse hover' cancel notification (WBChildFrame *, cancel any 'hover' action)
98 static int internal_is_ins_mode(WBChildFrame *); // returns non-zero if in 'insert' mode, 0 for 'overwrite'
99 static void internal_toggle_ins_mode(WBChildFrame *); // toggles insert mode on/off (WBChildFrame *, press 'INS' key)
100 static void internal_copy_to_cb(WBChildFrame *); // copy selection to clipboard
101 static void internal_paste_from_cb(WBChildFrame *); // paste from clipboard
102 static void internal_cut_to_cb(WBChildFrame *); // delete selection, copying to clipboard first
103 static void internal_delete_sel(WBChildFrame *); // delete selection only
104 static void internal_select_all(WBChildFrame *); // select all
105 static void internal_select_none(WBChildFrame *); // select none
106 static void internal_save(WBChildFrame *, const char *szFileName); // save to specified file name (WBChildFrame *, NULL to keep same file name)
107 static WB_PCSTR internal_get_file_name(WBChildFrame *); // get (const) pointer to file name string
108 static void internal_mouse_click(WBChildFrame *, int iX, int iY,
109  int iButtonMask, int iACS); // 'mouse click' notification. \sa aWM_POINTER
110 static void internal_mouse_dblclick(WBChildFrame *, int iX, int iY,
111  int iButtonMask, int iACS); // 'mouse double click' notification. \sa aWM_POINTER
112 static void internal_mouse_drag(WBChildFrame *, int iX, int iY,
113  int iButtonMask, int iACS); // 'mouse drag' (begin) notification. \sa aWM_POINTER
114 static void internal_mouse_drop(WBChildFrame *, int iX, int iY,
115  int iButtonMask, int iACS); // 'mouse drop' (drag end) notification. \sa aWM_POINTER
116 static void internal_mouse_move(WBChildFrame *, int iX, int iY); // 'mouse motion' notification. \sa aWM_POINTER
117 static void internal_mouse_scrollup(WBChildFrame *, int iX, int iY,
118  int iButtonMask, int iACS); // 'mouse scroll up' notification. \sa aWM_POINTER
119 static void internal_mouse_scrolldown(WBChildFrame *, int iX, int iY,
120  int iButtonMask, int iACS); // 'mouse scroll down' notification. \sa aWM_POINTER
121 static void internal_mouse_cancel(WBChildFrame *); // 'mouse cancel' notification (cancel 'drag', etc.). \sa aWM_POINTER
122 static void internal_get_row_col(WBChildFrame *pC, int *piR, int *piC);// get row/col (etc.)
123 static int internal_has_selection(WBChildFrame *pC); // returns non-zero value if there is a selection
124 static void internal_undo(WBChildFrame *); // perform an undo
125 static void internal_redo(WBChildFrame *); // perform a re-do
126 static int internal_can_undo(WBChildFrame *); // returns non-zero value if 'can undo'
127 static int internal_can_redo(WBChildFrame *); // returns non-zero value if 'can redo'
128 static int internal_is_empty(WBChildFrame *); // returns non-zero value if 'empty'
129 
130 
131 static void internal_update_status_text(WBEditWindow *); // called whenever status text should change
132 static void internal_new_cursor_pos(WBEditWindow *); // called whenever cursor position changes.
133 
134 
135 static XColor clrFG, clrBG, clrAFG, clrABG;
136 static int iInitColorFlag = 0;
137 
138 
139 static WBChildFrameUI internal_CFUI =
140 {
142  internal_do_char, internal_scancode, internal_bkspace, internal_del,
143  internal_tab, internal_enter, internal_uparrow, internal_downarrow,
144  internal_leftarrow, internal_rightarrow, internal_home, internal_end,
145  internal_pgup, internal_pgdown, internal_pgleft, internal_pgright,
146  internal_help, internal_hover_notify, internal_hover_cancel, internal_is_ins_mode,
147  internal_toggle_ins_mode, internal_copy_to_cb, internal_paste_from_cb, internal_cut_to_cb,
148  internal_delete_sel, internal_select_all, internal_select_none, internal_save,
149  internal_get_file_name, internal_mouse_click, internal_mouse_dblclick, internal_mouse_drag,
150  internal_mouse_drop, internal_mouse_move, internal_mouse_scrollup, internal_mouse_scrolldown,
151  internal_mouse_cancel, internal_get_row_col, internal_has_selection, internal_undo,
152  internal_redo, internal_can_undo, internal_can_redo, internal_is_empty
153 };
154 
155 
156 
174 
191 Atom aEW_EDIT_CHANGE=None;
192 
193 
194 
195 
196 #define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
197 #define LOAD_COLOR(X,Y,Z) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) <= 0){ WB_WARN_PRINT("%s - WARNING: can't find color %s, using default value %s\n", __FUNCTION__, X, Z); strcpy(Y,Z); }
198 
199 static void InternalCheckEWColorsAndAtoms(void)
200 {
201  Colormap colormap;
202 
203  // *Frame.background, *Frame.foreground, *WmFrame.background, *WmFrame.foreground,
204  // *Form.background, *Form.foreground, *background, *foreground
205 
206  if(aEW_EDIT_CHANGE == None)
207  {
208  aEW_EDIT_CHANGE = WBGetAtom(WBGetDefaultDisplay(), "EW_EDIT_CHANGE");
209  }
210 
211  if(aEW_HOVER_NOTIFY == None)
212  {
213  aEW_HOVER_NOTIFY = WBGetAtom(WBGetDefaultDisplay(), "EW_HOVER_NOTIFY");
214  }
215 
216  if(!iInitColorFlag)
217  {
218  char szFG[16], szBG[16], szAFG[16], szABG[16]; // note colors can typically be up to 13 characters + 0 byte
219 
220  colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
221 
222  // (these color names and standards have changed *WAY* too many times...)
223 
224  LOAD_COLOR0("*Text.foreground",szFG) else LOAD_COLOR0("*Edit.foreground", szFG)
225  else LOAD_COLOR("*foreground", szFG, "#000000");
226  LOAD_COLOR0("*Text.background",szBG) else LOAD_COLOR0("*Edit.background", szBG)
227  else LOAD_COLOR("*background", szBG, "white"); // pure white background by default
228 
229  LOAD_COLOR("selected_bg_color", szABG, "#0040FF"); // a slightly greenish blue for the 'selected' BG color
230  LOAD_COLOR("selected_fg_color", szAFG, "white"); // white FG when selected
231 
232  WB_ERROR_PRINT("TEMPORARY: %s - edit window colors: FG=%s BG=%s AFG=%s ABG=%s\n", __FUNCTION__,
233  szFG, szBG, szAFG, szABG);
234 
235  XParseColor(WBGetDefaultDisplay(), colormap, szFG, &clrFG);
236  XAllocColor(WBGetDefaultDisplay(), colormap, &clrFG);
237  XParseColor(WBGetDefaultDisplay(), colormap, szBG, &clrBG);
238  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBG);
239  XParseColor(WBGetDefaultDisplay(), colormap, szAFG, &clrAFG);
240  XAllocColor(WBGetDefaultDisplay(), colormap, &clrAFG);
241  XParseColor(WBGetDefaultDisplay(), colormap, szABG, &clrABG);
242  XAllocColor(WBGetDefaultDisplay(), colormap, &clrABG);
243 
244  iInitColorFlag = 1;
245  }
246 }
247 
248 
249 WBEditWindow *WBCreateEditWindow(WBFrameWindow *pOwner, XFontStruct *pFont,
250  const char *szFocusMenu, const WBFWMenuHandler *pHandlerArray,
251  int fFlags)
252 {
253 WBEditWindow *pRval;
254 XFontSet rFontSet;
255 Display *pDisplay;
256 int iRet;
257 
258 
259  InternalCheckEWColorsAndAtoms();
260 
261 
262  if(!pOwner)
263  {
264  return NULL;
265  }
266 
267  pDisplay = WBGetWindowDisplay(pOwner->wID);
268 
269  pRval = (WBEditWindow *)WBAlloc(sizeof(*pRval));
270 
271  if(!pRval)
272  {
273  WB_ERROR_PRINT("ERROR: %s - not enough memory\n", __FUNCTION__);
274  return NULL;
275  }
276 
277  bzero(pRval, sizeof(*pRval));
278 
279  pRval->ulTag = EDIT_WINDOW_TAG;
280  pRval->szFileName = NULL; // explicitly do this, though the bzero would've
281  pRval->pUserCallback = NULL; // explicitly do this, too
282 
283  if(!pFont)
284  {
285  rFontSet = None;//WBGetDefaultFontSet();
286  }
287  else
288  {
289  rFontSet = WBFontSetFromFont(pDisplay, pFont);
290  }
291 
293 
294 // pRval->xTextObject.vtable->set_col(&(pRval->xTextObject), 0);
295 // pRval->xTextObject.vtable->set_row(&(pRval->xTextObject), 0);
296 
297 
298  // create the actual window.
299 
300  iRet = FWInitChildFrame(&(pRval->childframe), pOwner, rFontSet, // NOTE: a copy of rFontSet will be in 'childframe.rFontSet'
301  szFocusMenu, pHandlerArray,
302  FWEditWindowEvent, fFlags);
303 
304  if(rFontSet != None)
305  {
306  XFreeFontSet(pDisplay, rFontSet);
307  rFontSet = None;
308  }
309 
310  if(iRet < 0)
311  {
312  WB_ERROR_PRINT("ERROR: %s - unable to initialize child frame\n", __FUNCTION__);
313 
314  WBFree(pRval);
315 
316  return NULL;
317  }
318 
319 
320  pRval->xTextObject.wIDOwner = pRval->childframe.wID; // TODO: make assigning this an API function?
321 
322 
323  // assign my 'UI' vtable pointer, which will be (intelligently) called by the 'Child Frame' event handler
324  // this standardizes the various UI methods and makes coding a complex UI quite a bit easier
325  pRval->childframe.pUI = &internal_CFUI; // UI function vtable
326 
327  // assign my 'destructor', which will be called by FWDestroyChildFrame
328  // THIS must be done LAST, since 'FWInitChildFrame' might call FWDestroyChildFrame on error
329  // and I don't want to call the 'destructor' yet.
330  pRval->childframe.destructor = InternalEditWindowDestructor;
331 
332 
333  // TODO: any other initialization belongs HERE. if error, call FWDestroyChildFrame() and return
334  // a NULL immediately (since that would destroy the child frame AND the Edit Window stuff)
335 
336 
337  internal_update_status_text(pRval); // update status text now.
338 
340  333333, 1, 1); // TODO: use #define for timer ID and period (1/3 second for now))
341  // NOTE: when I unregister the window callback, the timer will be deleted automatically
342 
343  return pRval;
344 }
345 
346 // this function destroys any allocated objects in the WBEditWindow, but doesn't free the pointer
347 static void InternalEditWindowDestroy(WBEditWindow *pEditWindow)
348 {
349  // these next 'things' are private to this particular 'class'
350 
351  if(pEditWindow->szFileName)
352  {
353  WBFree(pEditWindow->szFileName);
354  pEditWindow->szFileName = NULL;
355  }
356 
357  WBDestroyInPlaceTextObject(&(pEditWindow->xTextObject));
358 
359  pEditWindow->ulTag = 0; // not valid any more
360 }
361 
362 static void InternalEditWindowDestructor(WBChildFrame *pC)
363 {
364  WBEditWindow *pEW = (WBEditWindow *)pC;
365 
366 // WB_ERROR_PRINT("TEMPORARY: %s - destroying edit window %p\n", __FUNCTION__, pEW);
367 
368  InternalEditWindowDestroy(pEW);
369 
370  bzero(pEW, sizeof(*pEW)); // in case anything else 'stale' is there
371 
372  WBFree(pEW);
373 
374 // WB_ERROR_PRINT("TEMPORARY: %s - destroyed edit window %p\n", __FUNCTION__, pEW);
375 }
376 
378 {
379  if(!WBIsValidEditWindow(pEditWindow))
380  {
381  WB_ERROR_PRINT("ERROR: %s - invalid Edit Window pointer %p\n", __FUNCTION__, pEditWindow);
382  return;
383  }
384 
385  if(pEditWindow->pUserCallback)
386  {
387  // TODO: Send an aDESTROY_NOTIFY ClientMessage 'destroy' notification (once I document it)
388 
389  // TODO: send a 'DestroyNotify' event instead???
390 
391  pEditWindow->pUserCallback = NULL;
392  }
393 
394  if(pEditWindow->childframe.pUserCallback == FWEditWindowEvent)
395  {
396  pEditWindow->childframe.pUserCallback = NULL; // prevents any kind of recursion from messages (unlikely)
397  }
398 
399  if(pEditWindow->childframe.destructor == NULL)
400  {
401  WB_ERROR_PRINT("ERROR: %s - destructor is NULL - pointer will not be free'd\n", __FUNCTION__);
402 
403  InternalEditWindowDestroy(pEditWindow); // perform the necessary destruction *ANYWAY* but don't free the pointer
404 
405  // TODO: ALWAYS assign the destructor to NULL, and THEN call it directly after calling FWDestroyChildFrame() ?
406  }
407 
408  if(pEditWindow->childframe.wID != None)
409  {
411  pEditWindow->childframe.wID, 1); // the preferred method, when practical, is to delete it explicitly
412  }
413 
414  // the last step destroys the child frame, which will destroy the Edit Window as well
415 
416  FWDestroyChildFrame(&(pEditWindow->childframe)); // destroy window, free up all resources, call destructor (if not NULL)
417 }
418 
420 {
422 
423 
424  if(pRval && !WBIsValidEditWindow(pRval))
425  {
426  pRval = NULL;
427  }
428 
429  return pRval;
430 }
431 
432 
433 int WBEditWindowLoadFile(WBEditWindow *pEditWindow, const char *pszFileName)
434 {
435  if(!pEditWindow || !WBIsValidEditWindow(pEditWindow)
436  || !pszFileName || !*pszFileName)
437  {
438  return -1;
439  }
440 
441  // TODO: implement 'load file'. handle unicode files. UTF-16 files begin with 0xff, 0xfe
442  // UTF-8 files are assumed to be the same as ASCII (with no prefix).
443 
444 
445 
446  return -1; // error
447 }
448 
449 int WBEditWindowSaveFile(WBEditWindow *pEditWindow, const char *pszFileName)
450 {
451  if(!pEditWindow || !WBIsValidEditWindow(pEditWindow))
452  {
453  return -1;
454  }
455 
456  if(!pszFileName || !*pszFileName)
457  {
458  pszFileName = pEditWindow->szFileName;
459  }
460 
461  if(!pszFileName || !*pszFileName)
462  {
463  return -1; // error (no file name)
464  }
465 
466 
467  // TODO: implement 'file save'
468 
469 
470  return -1; // error
471 }
472 
473 void WBEditWindowClear(WBEditWindow *pEditWindow)
474 {
475  if(!pEditWindow || !WBIsValidEditWindow(pEditWindow))
476  {
477  return;
478  }
479 
480  WBDestroyInPlaceTextObject(&(pEditWindow->xTextObject));
481  WBInitializeInPlaceTextObject(&(pEditWindow->xTextObject), pEditWindow->childframe.wID);
482 
483  FWChildFrameRecalcLayout(&(pEditWindow->childframe));
484 }
485 
486 void WBEditWindowRegisterCallback(WBEditWindow *pEditWindow, WBWinEvent pUserCallback)
487 {
488  if(!pEditWindow || !WBIsValidEditWindow(pEditWindow))
489  {
490  return;
491  }
492 
493  pEditWindow->pUserCallback = pUserCallback;
494 }
495 
496 
497 
499 // //
500 // _____ _ _ _ _ _ //
501 // | ____|__ __ ___ _ __ | |_ | | | | __ _ _ __ __| || | ___ _ __ //
502 // | _| \ \ / // _ \| '_ \ | __| | |_| | / _` || '_ \ / _` || | / _ \| '__| //
503 // | |___ \ V /| __/| | | || |_ | _ || (_| || | | || (_| || || __/| | //
504 // |_____| \_/ \___||_| |_| \__| |_| |_| \__,_||_| |_| \__,_||_| \___||_| //
505 // //
506 // //
508 
509 
510 
511 int FWEditWindowEvent(Window wID, XEvent *pEvent)
512 {
513 WBEditWindow *pE;
514 GC gc;
515 WB_GEOM geom;//, geom2;
516 Display *pDisplay = WBGetWindowDisplay(wID);
517 //XFontSet xFontSet;
518 
519 
520  pE = WBEditWindowFromWindowID(wID);
521  if(!pE)
522  {
523  return 0;
524  }
525 
526  switch(pEvent->type)
527  {
528  case Expose:
529 // WB_ERROR_PRINT("TEMPORARY: %s - expose event\n", __FUNCTION__);
530  geom.x = pEvent->xexpose.x;
531  geom.y = pEvent->xexpose.y;
532  geom.width = pEvent->xexpose.width;
533  geom.height = pEvent->xexpose.height;
534 
535  // TEMPORARY - just erase the background, for now...
536  gc = WBBeginPaintGeom(wID, &geom);
537 
538  XSetBackground(pDisplay, gc, clrBG.pixel);
539 
540 // WBGetWindowGeom(wID, &geom2);
541 //
542 // // this is client geometry, not window geometry, so fix it
543 // geom2.x = 1; // 1-pixel border
544 // geom2.y = 1;
545 // geom2.width -= 2; // 1 pixel border on BOTH sides
546 // geom2.height -= 2;
547 //
548 // NOTE: for some reason this un-does what happens later in do_expose() ... maybe XSync after?
549 // XSetForeground(pDisplay, gc, clrBG.pixel);
550 // XFillRectangle(pDisplay, wID, gc, geom2.x, geom2.y, geom2.width, geom2.height);
551 
552  XSetForeground(pDisplay, gc, clrFG.pixel);
553 
554 // xFontSet = pE->childframe.rFontSet;
555 
556  pE->xTextObject.vtable->do_expose(&(pE->xTextObject), pDisplay, wID, gc,
557  &geom, // the GEOM to 'paint to'
558  NULL,//&geom2, // the GEOM bordering the window's viewport (NULL for ALL)
559  pE->childframe.rFontSet);
560  WBEndPaint(wID, gc);
561 
562 // if(xFontSet)
563 // {
564 // XFreeFontSet(pDisplay, xFontSet);
565 // }
566 
567  return 1; // "handled"
568 
569  case DestroyNotify:
570  // if I'm destroying ME, then I must free up the structure.
571  // if this callback is being called, assume NOT recursive.
572 
573  if(pEvent->xdestroywindow.window == wID)
574  {
575  pE->childframe.wID = None; // assign 'none' as the window ID, since I already destroyed it. don't re-destroy it.
576 
577  WBDestroyEditWindow(pE); // this should fix everything else.
578 
579  return 1; // handled
580  }
581 
582  break;
583 
584  case ClientMessage:
585  if(pEvent->xclient.message_type == aRESIZE_NOTIFY ||
586  pEvent->xclient.message_type == aRECALC_LAYOUT)
587  {
588  // TODO: process re-calculation of the extents, etc.
589 
591  pE->xTextObject.vtable->get_row(&(pE->xTextObject)),
592  pE->xTextObject.vtable->get_rows(&(pE->xTextObject)),
593  pE->xTextObject.vtable->get_col(&(pE->xTextObject)),
594  pE->xTextObject.vtable->get_cols(&(pE->xTextObject)),
596  WBFontSetDescent(pDisplay, pE->childframe.rFontSet)),
597  WBFontSetAvgCharWidth(pDisplay, pE->childframe.rFontSet));
598 // pE->childframe.pFont->ascent + pE->childframe.pFont->ascent + EDIT_WINDOW_LINE_SPACING,
599  }
600  else if(pEvent->xclient.message_type == aWM_TIMER)
601  {
602  // only when this tab is visible do I call the callback.
603 
604  if(pE->childframe.pOwner && // just in case
605  FWGetChildFrameIndex(pE->childframe.pOwner, NULL) // focus window's tab index
606  == FWGetChildFrameIndex(pE->childframe.pOwner, &(pE->childframe))) // THIS window's tab index
607  {
608 // WB_ERROR_PRINT("TEMPORARY: %s - timer\n", __FUNCTION__);
609 
610  pE->xTextObject.vtable->cursor_blink(&(pE->xTextObject), 1);
611  }
612  }
613  else if(pEvent->xclient.message_type == aQUERY_CLOSE)
614  {
615  if(pE->pUserCallback)
616  {
617  int iRval = pE->pUserCallback(wID, pEvent); // allow the user callback to determine when to close
618 
619  if(iRval)
620  {
621  return iRval;
622  }
623  }
624 
625  return 0; // for now, just return 'ok to close' whether I've saved or not
626  }
627  }
628 
629  return 0; // "not handled"
630 }
631 
632 
633 
635 // //
636 // _ _ ___ ____ _ _ _ _ //
637 // | | | ||_ _| / ___| __ _ | || || |__ __ _ ___ | | __ //
638 // | | | | | | | | / _` || || || '_ \ / _` | / __|| |/ / //
639 // | |_| | | | | |___| (_| || || || |_) || (_| || (__ | < //
640 // \___/ |___| \____|\__,_||_||_||_.__/ \__,_| \___||_|\_\ //
641 // //
642 // //
644 
645 
646 static void internal_update_status_text(WBEditWindow *pE) // called whenever cursor position changes.
647 {
648 WBChildFrame *pC = &(pE->childframe);
649 int iR, iC;
650 char tbuf[1024];
651 
652  CALLBACK_TRACKER;
653 
654  if(!WBIsValidEditWindow(pE))
655  {
656  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
657 
658  return;
659  }
660 
661  internal_get_row_col(pC, &iR, &iC);
662 
663  if(pC->szStatusText)
664  {
665  WBFree(pC->szStatusText);
666  pC->szStatusText = NULL;
667  }
668 
669  snprintf(tbuf, sizeof(tbuf), "Row,Col: %d,%d\tlines: %d width: %d\t%s\t",
670  iR,
671  iC,
672  pE->xTextObject.vtable->get_rows(&(pE->xTextObject)),
673  pE->xTextObject.vtable->get_cols(&(pE->xTextObject)),
674  (const char *)(pE->xTextObject.vtable->get_insmode(&(pE->xTextObject)) == InsertMode_INSERT ? "INS" :
675  (const char *)(pE->xTextObject.vtable->get_insmode(&(pE->xTextObject)) == InsertMode_OVERWRITE ? "OVR" : "???")));
676 
677  pC->szStatusText = WBCopyString(tbuf);
678 
679  if(!pC->szStatusText)
680  {
681  WB_ERROR_PRINT("ERROR: %s - not enough memory to display status\n", __FUNCTION__);
682  }
683 
685 }
686 
687 static void internal_new_cursor_pos(WBEditWindow *pE) // called whenever cursor position changes.
688 {
689  CALLBACK_TRACKER;
690 
691  if(WBIsValidEditWindow(pE))
692  {
693  internal_update_status_text(pE);
694 
695  // see if the row exceeds the viewport, and if that's the case, scroll it.
696 
697 
698 
699  // TODO: other things, like messing with the display area, re-calc layout, re-paint, etc.
700  // check to see if top/bottom rows changed, invalidate old line, validate new line, see if
701  // horizontal scrolling entire window or just the cursor, etc. etc. etc.
702  }
703 }
704 
705 static void internal_notify_change(WBChildFrame *pC, int bUndo)
706 {
707 WBEditWindow *pE = (WBEditWindow *)pC;
708 
709  if(!WBIsValidEditWindow(pE))
710  {
711  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
712 
713  return;
714  }
715 
716  if(pE->pUserCallback)
717  {
718  XClientMessageEvent evt;
719 
720  bzero(&evt, sizeof(evt));
721 
722  evt.type=ClientMessage;
723  evt.display=WBGetWindowDisplay(pC->wID);
724  evt.window=pC->wID;
725  evt.message_type=aEW_EDIT_CHANGE;
726  evt.format=32;
727 
728  evt.data.l[0] = bUndo;
729  evt.data.l[1] = pE->xTextObject.vtable->get_row(&(pE->xTextObject));
730  evt.data.l[2] = pE->xTextObject.vtable->get_col(&(pE->xTextObject));
731 
732  pE->pUserCallback(pC->wID, (XEvent *)&evt);
733  }
734 }
735 
736 static void internal_do_char(WBChildFrame *pC, XClientMessageEvent *pEvent)
737 {
738 WBEditWindow *pE = (WBEditWindow *)pC;
739 int iKey, iACS, nChar;
740 char *pBuf;
741 
742 
743  CALLBACK_TRACKER;
744 
745  if(!WBIsValidEditWindow(pE))
746  {
747  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
748 
749  return;
750  }
751 
752  // TODO: determine whether or not the character is printable
753 
754  iKey = pEvent->data.l[0]; // result from WBKeyEventProcessKey()
755  iACS = pEvent->data.l[1];
756  nChar = pEvent->data.l[2];
757  pBuf = (char *)&(pEvent->data.l[3]);
758 
759  if(iACS && (iACS & WB_KEYEVENT_ACSMASK) != WB_KEYEVENT_SHIFT) // only SHIFT can be used here
760  {
761  // TODO: handle non-printing chars
762 
763  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
764  WB_ERROR_PRINT("TEMPORARY: %s - beep\n", __FUNCTION__);
765  }
766  else if(pE->xTextObject.vtable->has_select(&(pE->xTextObject)))
767  {
768 // WB_ERROR_PRINT("TEMPORARY: %s - set text to \"%.*s\"\n", __FUNCTION__, nChar, pBuf);
769  pE->xTextObject.vtable->set_text(&(pE->xTextObject), pBuf, nChar);
770  internal_notify_change(pC, 0);
771  }
772  else
773  {
774 // WB_ERROR_PRINT("TEMPORARY: %s - inserting \"%.*s\"\n", __FUNCTION__, nChar, pBuf);
775  pE->xTextObject.vtable->ins_chars(&(pE->xTextObject), pBuf, nChar);
776  internal_notify_change(pC, 0);
777  }
778 
779 // {
780 // char *p1 = pE->xTextObject.vtable->get_text(&(pE->xTextObject));
781 //
782 // WB_ERROR_PRINT("TEMPORARY: %s - new text \"%s\"\n", __FUNCTION__, p1);
783 //
784 // if(p1)
785 // {
786 // WBFree(p1);
787 // }
788 // }
789 
790  internal_new_cursor_pos((WBEditWindow *)pC);
791 }
792 
793 static void internal_scancode(WBChildFrame *pC, XClientMessageEvent *pEvent)
794 {
795 WBEditWindow *pE = (WBEditWindow *)pC;
796 
797 
798  CALLBACK_TRACKER;
799 
800  if(!WBIsValidEditWindow(pE))
801  {
802  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
803 
804  return;
805  }
806 
807  // TODO: handle things I might want, like 'indent' which might be ctrl+[ or ctrl+], or maybe
808  // ctrl+tab or ctrl+shift+tab
809 
810 
811  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
812 }
813 
814 static void internal_bkspace(WBChildFrame *pC, int iACS)
815 {
816 WBEditWindow *pE = (WBEditWindow *)pC;
817 
818 
819  CALLBACK_TRACKER;
820 
821  if(!WBIsValidEditWindow(pE))
822  {
823  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
824 
825  return;
826  }
827 
828  if(iACS & WB_KEYEVENT_ACSMASK) // not handling ctrl, shift, or alt with backspace. yet.
829  {
830  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
831  }
832  else
833  {
834  pE->xTextObject.vtable->del_chars(&(pE->xTextObject), -1);
835  internal_notify_change(pC, 0);
836  }
837 
838  internal_new_cursor_pos((WBEditWindow *)pC);
839 }
840 
841 static void internal_del(WBChildFrame *pC, int iACS)
842 {
843 WBEditWindow *pE = (WBEditWindow *)pC;
844 
845 
846  CALLBACK_TRACKER;
847 
848  if(!WBIsValidEditWindow(pE))
849  {
850  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
851 
852  return;
853  }
854 
855  // shift+del does 'cut' behavior
856 
857  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift+del
858  {
859  if(!pE->xTextObject.vtable->has_select(&(pE->xTextObject)))
860  {
861  // no selection, can't "cut"
862  XBell(WBGetWindowDisplay(pC->wID), -100);
863  }
864  else
865  {
866  // copy selection to the clipboard, then delete - same as internal_cut_to_cb()
867 
868  char *p1 = pE->xTextObject.vtable->get_text(&(pE->xTextObject));
869  if(p1)
870  {
871  WBSetClipboardData(WBGetWindowDisplay(pC->wID), aUTF8_STRING, 8, p1, strlen(p1) + 1);
872 
873  WBFree(p1);
874 
876  internal_notify_change(pC, 0);
877  }
878  }
879  }
880  else if(pE->xTextObject.vtable->has_select(&(pE->xTextObject)))
881  {
883  internal_notify_change(pC, 0);
884  }
885  else
886  {
887  pE->xTextObject.vtable->del_chars(&(pE->xTextObject), 1);
888  internal_notify_change(pC, 0);
889  }
890 
891  internal_new_cursor_pos((WBEditWindow *)pC);
892 }
893 
894 static void internal_tab(WBChildFrame *pC, int iACS)
895 {
896 WBEditWindow *pE = (WBEditWindow *)pC;
897 
898 
899  CALLBACK_TRACKER;
900 
901  if(!WBIsValidEditWindow(pE))
902  {
903  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
904 
905  return;
906  }
907 
908  // if there's a selection, indent it??
909 
910  if(iACS & WB_KEYEVENT_ACSMASK) // not handling ctrl, shift, or alt with tab. yet.
911  {
912  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
913  }
914  else
915  {
916  pE->xTextObject.vtable->ins_chars(&(pE->xTextObject), "\t", 1);
917  internal_notify_change(pC, 0);
918  }
919 
920  internal_new_cursor_pos((WBEditWindow *)pC);
921 }
922 
923 static void internal_enter(WBChildFrame *pC, int iACS)
924 {
925 WBEditWindow *pE = (WBEditWindow *)pC;
926 
927 
928  CALLBACK_TRACKER;
929 
930  if(!WBIsValidEditWindow(pE))
931  {
932  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
933 
934  return;
935  }
936 
937  if(iACS & WB_KEYEVENT_ACSMASK) // not handling ctrl, shift, or alt with 'enter'. yet.
938  {
939  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
940  }
941  else
942  {
943  pE->xTextObject.vtable->ins_chars(&(pE->xTextObject), "\n", 1);
944  internal_notify_change(pC, 0);
945  }
946 
947  internal_new_cursor_pos((WBEditWindow *)pC);
948 }
949 
950 static void internal_uparrow(WBChildFrame *pC, int iACS)
951 {
952 WBEditWindow *pE = (WBEditWindow *)pC;
953 
954 
955  CALLBACK_TRACKER;
956 
957  if(!WBIsValidEditWindow(pE))
958  {
959  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
960 
961  return;
962  }
963 
964  if(iACS & WB_KEYEVENT_ACSMASK) // not handling ctrl, shift, or alt with 'up'. yet.
965  {
966  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
967  }
968  else
969  {
971  }
972 
973  internal_new_cursor_pos((WBEditWindow *)pC);
974 }
975 
976 static void internal_downarrow(WBChildFrame *pC, int iACS)
977 {
978 WBEditWindow *pE = (WBEditWindow *)pC;
979 
980 
981  CALLBACK_TRACKER;
982 
983  if(!WBIsValidEditWindow(pE))
984  {
985  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
986 
987  return;
988  }
989 
990  if(iACS & WB_KEYEVENT_ACSMASK) // not handling ctrl, shift, or alt with 'down'. yet.
991  {
992  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
993  }
994  else
995  {
997  }
998 
999  internal_new_cursor_pos((WBEditWindow *)pC);
1000 }
1001 
1002 static void internal_leftarrow(WBChildFrame *pC, int iACS)
1003 {
1004 WBEditWindow *pE = (WBEditWindow *)pC;
1005 
1006 
1007  CALLBACK_TRACKER;
1008 
1009  if(!WBIsValidEditWindow(pE))
1010  {
1011  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1012 
1013  return;
1014  }
1015 
1016  if(iACS & WB_KEYEVENT_ACSMASK) // not handling ctrl, shift, or alt with 'left'. yet.
1017  {
1018  WB_ERROR_PRINT("TEMPORARY: %s - iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
1019 
1020  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1021  }
1022  else
1023  {
1025  }
1026 
1027  internal_new_cursor_pos((WBEditWindow *)pC);
1028 }
1029 
1030 static void internal_rightarrow(WBChildFrame *pC, int iACS)
1031 {
1032 WBEditWindow *pE = (WBEditWindow *)pC;
1033 
1034 
1035  CALLBACK_TRACKER;
1036 
1037  if(!WBIsValidEditWindow(pE))
1038  {
1039  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1040 
1041  return;
1042  }
1043 
1044  if(iACS & WB_KEYEVENT_ACSMASK) // not handling ctrl, shift, or alt with 'right'. yet.
1045  {
1046  WB_ERROR_PRINT("TEMPORARY: %s - iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
1047 
1048  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1049  }
1050  else
1051  {
1053  }
1054 
1055  internal_new_cursor_pos((WBEditWindow *)pC);
1056 }
1057 
1058 static void internal_home(WBChildFrame *pC, int iACS)
1059 {
1060 WBEditWindow *pE = (WBEditWindow *)pC;
1061 
1062 
1063  CALLBACK_TRACKER;
1064 
1065  if(!WBIsValidEditWindow(pE))
1066  {
1067  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1068 
1069  return;
1070  }
1071 
1072  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // control+end
1073  {
1075  }
1076  else if(iACS & WB_KEYEVENT_ACSMASK) // not handling shift, or alt with 'home'. yet.
1077  {
1078  WB_ERROR_PRINT("TEMPORARY: %s - iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
1079 
1080  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1081  }
1082  else
1083  {
1085  }
1086 
1087  internal_new_cursor_pos((WBEditWindow *)pC);
1088 }
1089 
1090 static void internal_end(WBChildFrame *pC, int iACS)
1091 {
1092 WBEditWindow *pE = (WBEditWindow *)pC;
1093 
1094 
1095  CALLBACK_TRACKER;
1096 
1097  if(!WBIsValidEditWindow(pE))
1098  {
1099  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1100 
1101  return;
1102  }
1103 
1104  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // control+end
1105  {
1107  }
1108  else if(iACS & WB_KEYEVENT_ACSMASK) // not handling shift, or alt with 'end'. yet.
1109  {
1110  WB_ERROR_PRINT("TEMPORARY: %s - iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
1111 
1112  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1113  }
1114  else
1115  {
1117  }
1118 
1119  internal_new_cursor_pos((WBEditWindow *)pC);
1120 }
1121 
1122 static void internal_pgup(WBChildFrame *pC, int iACS)
1123 {
1124 WBEditWindow *pE = (WBEditWindow *)pC;
1125 
1126 
1127  CALLBACK_TRACKER;
1128 
1129  if(!WBIsValidEditWindow(pE))
1130  {
1131  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1132 
1133  return;
1134  }
1135 
1136  if(iACS) // not handling ctrl, shift, or alt with 'enter'. yet.
1137  {
1138  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1139  }
1140  else
1141  {
1142  pE->xTextObject.vtable->page_up(&(pE->xTextObject));
1143  }
1144 
1145  internal_new_cursor_pos((WBEditWindow *)pC);
1146 }
1147 
1148 static void internal_pgdown(WBChildFrame *pC, int iACS)
1149 {
1150 WBEditWindow *pE = (WBEditWindow *)pC;
1151 
1152 
1153  CALLBACK_TRACKER;
1154 
1155  if(!WBIsValidEditWindow(pE))
1156  {
1157  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1158 
1159  return;
1160  }
1161 
1162  if(iACS) // not handling ctrl, shift, or alt with 'enter'. yet.
1163  {
1164  XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1165  }
1166  else
1167  {
1168  pE->xTextObject.vtable->page_down(&(pE->xTextObject));
1169  }
1170 
1171  internal_new_cursor_pos((WBEditWindow *)pC);
1172 }
1173 
1174 static void internal_pgleft(WBChildFrame *pC, int iACS)
1175 {
1176 WBEditWindow *pE = (WBEditWindow *)pC;
1177 
1178 
1179  CALLBACK_TRACKER;
1180 
1181  if(!WBIsValidEditWindow(pE))
1182  {
1183  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1184 
1185  return;
1186  }
1187 
1188 // if(iACS) // not handling ctrl, shift, or alt with 'enter'. yet.
1189 // {
1190 // XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1191 // }
1192 // else
1193  {
1194  pE->xTextObject.vtable->page_left(&(pE->xTextObject));
1195  }
1196 
1197  internal_new_cursor_pos((WBEditWindow *)pC);
1198 }
1199 
1200 static void internal_pgright(WBChildFrame *pC, int iACS)
1201 {
1202 WBEditWindow *pE = (WBEditWindow *)pC;
1203 
1204 
1205  CALLBACK_TRACKER;
1206 
1207  if(!WBIsValidEditWindow(pE))
1208  {
1209  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1210 
1211  return;
1212  }
1213 
1214 // if(iACS) // not handling ctrl, shift, or alt with 'enter'. yet.
1215 // {
1216 // XBell(WBGetWindowDisplay(pC->wID), -100); // for now give audible feedback that I'm ignoring it
1217 // }
1218 // else
1219  {
1221  }
1222 
1223  internal_new_cursor_pos((WBEditWindow *)pC);
1224 }
1225 
1226 static void internal_help(WBChildFrame *pC, int iACS)
1227 {
1228 WBEditWindow *pE = (WBEditWindow *)pC;
1229 
1230 
1231  CALLBACK_TRACKER;
1232 
1233  if(!WBIsValidEditWindow(pE))
1234  {
1235  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1236 
1237  return;
1238  }
1239 
1240 }
1241 
1242 static void internal_hover_notify(WBChildFrame *pC, int x, int y)
1243 {
1244 WBEditWindow *pE = (WBEditWindow *)pC;
1245 
1246 
1247  CALLBACK_TRACKER;
1248 
1249  if(!WBIsValidEditWindow(pE))
1250  {
1251  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1252 
1253  return;
1254  }
1255 
1256 }
1257 
1258 static void internal_hover_cancel(WBChildFrame *pC)
1259 {
1260 WBEditWindow *pE = (WBEditWindow *)pC;
1261 
1262 
1263  CALLBACK_TRACKER;
1264 
1265  if(!WBIsValidEditWindow(pE))
1266  {
1267  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1268 
1269  return;
1270  }
1271 
1272 }
1273 
1274 static int internal_is_ins_mode(WBChildFrame *pC)
1275 {
1276 WBEditWindow *pE = (WBEditWindow *)pC;
1277 
1278 
1279  CALLBACK_TRACKER;
1280 
1281  if(!WBIsValidEditWindow(pE))
1282  {
1283  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1284 
1285  return -1;
1286  }
1287 
1289 }
1290 
1291 static void internal_toggle_ins_mode(WBChildFrame *pC)
1292 {
1293 WBEditWindow *pE = (WBEditWindow *)pC;
1294 int iInsMode;
1295 
1296 
1297  CALLBACK_TRACKER;
1298 
1299  if(!WBIsValidEditWindow(pE))
1300  {
1301  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1302 
1303  return;
1304  }
1305 
1306  iInsMode = pE->xTextObject.vtable->get_insmode(&(pE->xTextObject)) == InsertMode_INSERT;
1308 
1309  internal_new_cursor_pos((WBEditWindow *)pC);
1310  internal_update_status_text(pE);
1311 }
1312 
1313 static void internal_copy_to_cb(WBChildFrame *pC)
1314 {
1315 WBEditWindow *pE = (WBEditWindow *)pC;
1316 char *p1;
1317 
1318 
1319  CALLBACK_TRACKER;
1320 
1321  if(!WBIsValidEditWindow(pE))
1322  {
1323  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1324 
1325  return;
1326  }
1327 
1328  // copy selection to the clipboard
1329 
1330  if(pE->xTextObject.vtable->has_select(&(pE->xTextObject)))
1331  {
1332  p1 = pE->xTextObject.vtable->get_text(&(pE->xTextObject));
1333 
1334  if(p1)
1335  {
1336  WBSetClipboardData(WBGetWindowDisplay(pC->wID), aUTF8_STRING, 8, p1, strlen(p1) + 1);
1337 
1338  WBFree(p1);
1339  }
1340  }
1341  else
1342  {
1343  // no selection, can't "copy"
1344 
1345  XBell(WBGetWindowDisplay(pC->wID), -100);
1346  }
1347 }
1348 
1349 static void internal_paste_from_cb(WBChildFrame *pC)
1350 {
1351 WBEditWindow *pE = (WBEditWindow *)pC;
1352 Atom aType = aUTF8_STRING;
1353 int iFormat = 8;
1354 unsigned long nData = 0;
1355 char *p1;
1356 
1357 
1358  CALLBACK_TRACKER;
1359 
1360  if(!WBIsValidEditWindow(pE))
1361  {
1362  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1363 
1364  return;
1365  }
1366 
1367  // copy selection to the clipboard, then delete - same as internal_cut_to_cb()
1368 
1369  p1 = (char *)WBGetClipboardData(WBGetWindowDisplay(pC->wID), &aType, &iFormat, &nData);
1370  if(!p1) // try regular string, not UTF8
1371  {
1372  aType = aSTRING;
1373  p1 = (char *)WBGetClipboardData(WBGetWindowDisplay(pC->wID), &aType, &iFormat, &nData);
1374  }
1375 
1376  if(p1 && nData > 0)
1377  {
1378  if(!p1[nData - 1])
1379  {
1380  nData--;
1381  }
1382  }
1383 
1384 
1385  if(p1)
1386  {
1387  // TODO: convert to correct format (ASCII)
1388  if(iFormat != 8) // 16-bit unicode is assumed now
1389  {
1390  if(iFormat == 8 * sizeof(wchar_t))
1391  {
1392  char *pNew = WBAlloc(sizeof(wchar_t) * (nData + 2));
1393  if(pNew)
1394  {
1395  bzero(pNew, sizeof(wchar_t) * (nData + 2));
1396  wcstombs(pNew, (const wchar_t *)p1, sizeof(wchar_t) * (nData + 2));
1397  }
1398 
1399  WBFree(p1);
1400  p1 = pNew;
1401  nData = strlen(p1);
1402  }
1403  else
1404  {
1405  XBell(WBGetWindowDisplay(pC->wID), -100);
1406  WB_ERROR_PRINT("TEMPORARY - %s - clipboard format %d, can't 'PASTE'\n", __FUNCTION__, iFormat);
1407 
1408  WBFree(p1);
1409  p1 = NULL; // by convention - also, checked in next section
1410  }
1411  }
1412 
1413  if(p1)
1414  {
1415  if(pE->xTextObject.vtable->has_select(&(pE->xTextObject)))
1416  {
1417  pE->xTextObject.vtable->replace_select(&(pE->xTextObject), p1, nData);
1418  internal_notify_change(pC, 0);
1419  }
1420  else
1421  {
1422  pE->xTextObject.vtable->ins_chars(&(pE->xTextObject), p1, nData);
1423  internal_notify_change(pC, 0);
1424  }
1425 
1426  WBFree(p1);
1427  p1 = NULL; // by convention
1428  }
1429  }
1430 
1431 }
1432 
1433 static void internal_cut_to_cb(WBChildFrame *pC)
1434 {
1435 WBEditWindow *pE = (WBEditWindow *)pC;
1436 
1437 
1438  CALLBACK_TRACKER;
1439 
1440  if(!WBIsValidEditWindow(pE))
1441  {
1442  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1443 
1444  return;
1445  }
1446 
1447  if(!pE->xTextObject.vtable->has_select(&(pE->xTextObject)))
1448  {
1449  // no selection, can't "cut"
1450  XBell(WBGetWindowDisplay(pC->wID), -100);
1451  }
1452  else
1453  {
1454  // copy selection to the clipboard, then delete - same as internal_cut_to_cb()
1455 
1456  char *p1 = pE->xTextObject.vtable->get_text(&(pE->xTextObject));
1457  if(p1)
1458  {
1459  WBSetClipboardData(WBGetWindowDisplay(pC->wID), aUTF8_STRING, 8, p1, strlen(p1) + 1);
1460 
1461  WBFree(p1);
1462 
1464  internal_notify_change(pC, 0);
1465  }
1466  }
1467 
1468 }
1469 
1470 static void internal_delete_sel(WBChildFrame *pC)
1471 {
1472 WBEditWindow *pE = (WBEditWindow *)pC;
1473 
1474 
1475  CALLBACK_TRACKER;
1476 
1477  if(!WBIsValidEditWindow(pE))
1478  {
1479  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1480 
1481  return;
1482  }
1483 
1484  if(!pE->xTextObject.vtable->has_select(&(pE->xTextObject)))
1485  {
1486  // no selection, can't "cut"
1487  XBell(WBGetWindowDisplay(pC->wID), -100);
1488  }
1489  else
1490  {
1492  internal_notify_change(pC, 0);
1493  }
1494 }
1495 
1496 static void internal_select_all(WBChildFrame *pC)
1497 {
1498 WBEditWindow *pE = (WBEditWindow *)pC;
1499 WB_RECT rct;
1500 
1501 
1502  CALLBACK_TRACKER;
1503 
1504  if(!WBIsValidEditWindow(pE))
1505  {
1506  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1507 
1508  return;
1509  }
1510 
1511  rct.left = rct.top = 0;
1512  rct.right = pE->xTextObject.vtable->get_cols(&(pE->xTextObject));
1513  rct.bottom = pE->xTextObject.vtable->get_rows(&(pE->xTextObject));
1514 
1515  pE->xTextObject.vtable->set_select(&(pE->xTextObject), &rct); // select 'all'
1516 }
1517 
1518 static void internal_select_none(WBChildFrame *pC)
1519 {
1520 WBEditWindow *pE = (WBEditWindow *)pC;
1521 
1522 
1523  CALLBACK_TRACKER;
1524 
1525  if(!WBIsValidEditWindow(pE))
1526  {
1527  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1528 
1529  return;
1530  }
1531 
1532  pE->xTextObject.vtable->set_select(&(pE->xTextObject), NULL);
1533 }
1534 
1535 static void internal_save(WBChildFrame *pC, const char *szFileName)
1536 {
1537 WBEditWindow *pE = (WBEditWindow *)pC;
1538 
1539 
1540  CALLBACK_TRACKER;
1541 
1542  if(!WBIsValidEditWindow(pE))
1543  {
1544  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1545 
1546  return;
1547  }
1548 
1549  WBEditWindowSaveFile(pE, szFileName);
1550 }
1551 
1552 static WB_PCSTR internal_get_file_name(WBChildFrame *pC)
1553 {
1554 WBEditWindow *pE = (WBEditWindow *)pC;
1555 
1556 
1557  CALLBACK_TRACKER;
1558 
1559  if(!WBIsValidEditWindow(pE))
1560  {
1561  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1562 
1563  return NULL;
1564  }
1565 
1566  return pE->szFileName;
1567 }
1568 
1569 static void internal_mouse_click(WBChildFrame *pC, int iX, int iY, int iButtonMask, int iACS)
1570 {
1571 WBEditWindow *pE = (WBEditWindow *)pC;
1572 
1573 
1574  CALLBACK_TRACKER;
1575 
1576  if(!WBIsValidEditWindow(pE))
1577  {
1578  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1579 
1580  return;
1581  }
1582 
1583  pE->xTextObject.vtable->mouse_click(&(pE->xTextObject), iX, iY, iButtonMask, iACS);
1584 }
1585 
1586 static void internal_mouse_dblclick(WBChildFrame *pC, int iX, int iY, int iButtonMask, int iACS)
1587 {
1588 WBEditWindow *pE = (WBEditWindow *)pC;
1589 
1590 
1591  CALLBACK_TRACKER;
1592 
1593  if(!WBIsValidEditWindow(pE))
1594  {
1595  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1596 
1597  return;
1598  }
1599 
1600 }
1601 
1602 static void internal_mouse_drag(WBChildFrame *pC, int iX, int iY, int iButtonMask, int iACS)
1603 {
1604 WBEditWindow *pE = (WBEditWindow *)pC;
1605 
1606 
1607  CALLBACK_TRACKER;
1608 
1609  if(!WBIsValidEditWindow(pE))
1610  {
1611  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1612 
1613  return;
1614  }
1615 
1617 }
1618 
1619 static void internal_mouse_drop(WBChildFrame *pC, int iX, int iY, int iButtonMask, int iACS)
1620 {
1621 WBEditWindow *pE = (WBEditWindow *)pC;
1622 
1623 
1624  CALLBACK_TRACKER;
1625 
1626  if(!WBIsValidEditWindow(pE))
1627  {
1628  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1629 
1630  return;
1631  }
1632 
1634 }
1635 
1636 static void internal_mouse_move(WBChildFrame *pC, int iX, int iY)
1637 {
1638 WBEditWindow *pE = (WBEditWindow *)pC;
1639 
1640 
1641  CALLBACK_TRACKER;
1642 
1643  if(!WBIsValidEditWindow(pE))
1644  {
1645  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1646 
1647  return;
1648  }
1649 
1650  pE->xTextObject.vtable->mouse_click(&(pE->xTextObject), iX, iY, 0, 0); // report mouse motion
1651 }
1652 
1653 static void internal_mouse_scrollup(WBChildFrame *pC, int iX, int iY, int iButtonMask, int iACS)
1654 {
1655 WBEditWindow *pE = (WBEditWindow *)pC;
1656 
1657 
1658  CALLBACK_TRACKER;
1659 
1660  if(!WBIsValidEditWindow(pE))
1661  {
1662  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1663 
1664  return;
1665  }
1666 
1667 }
1668 
1669 static void internal_mouse_scrolldown(WBChildFrame *pC, int iX, int iY, int iButtonMask, int iACS)
1670 {
1671 WBEditWindow *pE = (WBEditWindow *)pC;
1672 
1673 
1674  CALLBACK_TRACKER;
1675 
1676  if(!WBIsValidEditWindow(pE))
1677  {
1678  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1679 
1680  return;
1681  }
1682 
1683 }
1684 
1685 static void internal_mouse_cancel(WBChildFrame *pC)
1686 {
1687 WBEditWindow *pE = (WBEditWindow *)pC;
1688 
1689 
1690  CALLBACK_TRACKER;
1691 
1692  if(!WBIsValidEditWindow(pE))
1693  {
1694  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1695 
1696  return;
1697  }
1698 
1699 }
1700 
1701 static void internal_get_row_col(WBChildFrame *pC, int *piR, int *piC)
1702 {
1703 WBEditWindow *pE = (WBEditWindow *)pC;
1704 
1705 
1706  CALLBACK_TRACKER;
1707 
1708  if(!WBIsValidEditWindow(pE))
1709  {
1710  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1711 
1712  return;
1713  }
1714 
1715  if(piC)
1716  {
1717  *piC = pE->xTextObject.vtable->get_col(&(pE->xTextObject));
1718  }
1719 
1720  if(piR)
1721  {
1722  *piR = pE->xTextObject.vtable->get_row(&(pE->xTextObject));
1723  }
1724 }
1725 
1726 static int internal_has_selection(WBChildFrame *pC)
1727 {
1728 WBEditWindow *pE = (WBEditWindow *)pC;
1729 
1730 
1731  CALLBACK_TRACKER;
1732 
1733  if(!WBIsValidEditWindow(pE))
1734  {
1735  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1736 
1737  return 0;
1738  }
1739 
1740  return 0; // no current selection
1741 }
1742 
1743 static void internal_undo(WBChildFrame *pC)
1744 {
1745 WBEditWindow *pE = (WBEditWindow *)pC;
1746 
1747 
1748  CALLBACK_TRACKER;
1749 
1750  if(!WBIsValidEditWindow(pE))
1751  {
1752  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1753 
1754  return;
1755  }
1756 
1757  // perform an un-do operation
1758 }
1759 
1760 static void internal_redo(WBChildFrame *pC)
1761 {
1762 WBEditWindow *pE = (WBEditWindow *)pC;
1763 
1764 
1765  CALLBACK_TRACKER;
1766 
1767  if(!WBIsValidEditWindow(pE))
1768  {
1769  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1770 
1771  return;
1772  }
1773 
1774  // perform a re-do operation
1775 }
1776 
1777 static int internal_can_undo(WBChildFrame *pC)
1778 {
1779 WBEditWindow *pE = (WBEditWindow *)pC;
1780 
1781 
1782  CALLBACK_TRACKER;
1783 
1784  if(!WBIsValidEditWindow(pE))
1785  {
1786  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1787 
1788  return 0;
1789  }
1790 
1791  return 0; // can't un-do
1792 }
1793 
1794 static int internal_can_redo(WBChildFrame *pC)
1795 {
1796 WBEditWindow *pE = (WBEditWindow *)pC;
1797 
1798 
1799  CALLBACK_TRACKER;
1800 
1801  if(!WBIsValidEditWindow(pE))
1802  {
1803  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1804 
1805  return 0;
1806  }
1807 
1808  return 0; // can't re-do
1809 }
1810 
1811 
1812 static int internal_is_empty(WBChildFrame *pC)
1813 {
1814 WBEditWindow *pE = (WBEditWindow *)pC;
1815 
1816 
1817  CALLBACK_TRACKER;
1818 
1819  if(!WBIsValidEditWindow(pE))
1820  {
1821  WB_ERROR_PRINT("ERROR: %s - WBChildFrame and/or WBEditWindow not valid, %p\n", __FUNCTION__, pE);
1822 
1823  return -1; // return '-1' on error
1824  }
1825 
1826  // if the contents are NOT NULL, it's not 'empty'
1827 
1828  if((pE->xTextObject.vtable->get_rows && pE->xTextObject.vtable->get_rows(&(pE->xTextObject)) > 0) ||
1829  (pE->xTextObject.vtable->get_cols && pE->xTextObject.vtable->get_cols(&(pE->xTextObject)) > 0))
1830  {
1831  return 0; // NOT empty
1832  }
1833 
1834  return 0; // empty (for now; later, do I dive directly into xTextObject ??? new API for vtable?)
1835 }
1836