root/trunk/txlib/txwikey.c

Revision 11, 59.4 kB (checked in by jvw, 3 years ago)

DFSee 8.01 level; fix crash on huge PATH; History-popup improved (4OS2 compatible)

Line 
1//
2//                     TxWin, Textmode Windowing Library
3//
4//   Original code Copyright (c) 1995-2005 Fsys Software and Jan van Wijk
5//
6// ==========================================================================
7//
8// This file contains Original Code and/or Modifications of Original Code as
9// defined in and that are subject to the GNU Lesser General Public License.
10// You may not use this file except in compliance with the License.
11// BY USING THIS FILE YOU AGREE TO ALL TERMS AND CONDITIONS OF THE LICENSE.
12// A copy of the License is provided with the Original Code and Modifications,
13// and is also available at http://www.dfsee.com/txwin/lgpl.htm
14//
15// This library is free software; you can redistribute it and/or modify
16// it under the terms of the GNU Lesser General Public License as published
17// by the Free Software Foundation; either version 2.1 of the License,
18// or (at your option) any later version.
19//
20// This library is distributed in the hope that it will be useful,
21// but WITHOUT ANY WARRANTY; without even the implied warranty of
22// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23// See the GNU Lesser General Public License for more details.
24//
25// You should have received a copy of the GNU Lesser General Public License
26// along with this library; (lgpl.htm) if not, write to the Free Software
27// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28//
29// Questions on TxWin licensing can be directed to: txwin@fsys.nl
30//
31// ==========================================================================
32//
33// TX keyboard (TXWM_CHAR) processing for window-classes
34//
35// Author: J. van Wijk
36//
37// JvW  06-07-95   Initial version
38
39#include <memory.h>
40
41#include <txlib.h>                              // public interface
42#include <txwpriv.h>                            // private window interface
43
44/*****************************************************************************/
45// Input key handling for the TEXTVIEW window class
46/*****************************************************************************/
47ULONG txwIkeyTextview                           // RET   result
48(
49   TXWHANDLE           hwnd,                    // IN    current window
50   ULONG               mp1,                     // IN    param 1
51   ULONG               mp2                      // IN    param 2
52)
53{
54   ULONG               rc  = NO_ERROR;
55   TXWINBASE          *wnd = (TXWINBASE *) hwnd;
56   TXWINDOW           *win = wnd->window;
57   short               sx  = win->client.right  - win->client.left +1;
58   short               sy  = win->client.bottom - win->client.top  +1;
59   BOOL                upd = FALSE;             // window draw required
60   BOOL                skp = FALSE;
61   short               length;
62   short               nr;                      // nr of lines in buf
63   TXTEXTVIEW         *dat;
64   char              **s;
65
66   ENTER();
67   if ((wnd != NULL) && (win != NULL))
68   {
69      dat = &win->tv;
70      dat->maxtop = 0;
71      dat->maxcol = 0;
72      for (nr = 0, s = dat->buf; s && *s; s++)  // count entries
73      {
74         length = txSlen( *s);
75         dat->maxcol = (USHORT) max( (short) dat->maxcol, length);
76         nr++;
77      }
78      if (nr > sy)                              // text longer than window
79      {
80         dat->maxtop = nr - sy +1;              // allow one more than size
81      }
82      if ((short) dat->maxcol > sx)             // text wider as window
83      {
84         dat->maxcol = dat->maxcol - sx +1;     // allow one more than size
85      }
86      else
87      {
88         dat->maxcol = 0;
89      }
90      TRACES(("top:%4lu, left:%4hd, max:%4lu, lines:%4lu\n",
91               dat->topline, dat->leftcol, dat->maxtop, nr));
92      switch (mp2)                              // key value
93      {
94         case TXk_UP:
95            if (dat->topline != 0)
96            {
97               dat->topline--;
98               upd = TRUE;                      // full update (status)
99            }
100            break;
101
102         case TXk_DOWN:
103            if (dat->topline < dat->maxtop)
104            {
105               dat->topline++;
106               upd = TRUE;                      // full update (status)
107            }
108            break;
109
110         case TXk_PGUP:
111            if (dat->topline > (ULONG) sy)
112            {
113               dat->topline -= sy;
114
115               //- keep scrolling up until last line non-empty, or until the
116               //- TOP line contains a # at start of line (#help files only)
117               while ((strlen(win->tv.buf[win->tv.topline +sy -1]) ==  0 ) &&
118                      (      (win->tv.buf[win->tv.topline][0])     != '#') &&
119                      (      (win->tv.buf[              0][0])     == '#') &&
120                      (dat->topline > (ULONG) sy))
121               {
122                  dat->topline--;
123               }
124               upd = TRUE;
125            }
126            else if (dat->topline != 0)
127            {
128               dat->topline = 0;
129               upd = TRUE;
130            }
131            break;
132
133         case TXk_PGDN:
134            if (dat->topline + sy <= dat->maxtop)
135            {
136               dat->topline += sy;
137
138               //- keep scrolling down until first line non-empty
139               while ((strlen(win->tv.buf[win->tv.topline]) == 0) &&
140                      (dat->topline + 1 <= dat->maxtop))
141               {
142                  dat->topline++;
143               }
144               upd = TRUE;
145            }
146            else if (dat->topline != dat->maxtop)
147            {
148               dat->topline = dat->maxtop;
149               upd = TRUE;
150            }
151            break;
152
153         case TXc_HOME:
154            if (dat->topline != 0)
155            {
156               dat->topline = 0;
157               upd = TRUE;
158            }
159            break;
160
161         case TXc_END:
162            if (dat->topline != dat->maxtop)
163            {
164               dat->topline = dat->maxtop;
165               upd = TRUE;
166            }
167            break;
168
169         case '<':
170         case ',':
171         case TXa_COMMA:
172         case TXk_LEFT:
173            if (dat->leftcol != 0)
174            {
175               dat->leftcol--;
176               upd = TRUE;
177            }
178            break;
179
180         case '>':
181         case '.':
182         case TXa_DOT:
183         case TXk_RIGHT:
184            if (dat->leftcol < dat->maxcol)
185            {
186               dat->leftcol++;
187               upd = TRUE;
188            }
189            break;
190
191         case TXk_HOME:
192            if (dat->leftcol != 0)
193            {
194               dat->leftcol = 0;
195               upd = TRUE;
196            }
197            break;
198
199         case TXk_END:
200            if (dat->leftcol != dat->maxcol)
201            {
202               dat->leftcol = dat->maxcol;
203               upd = TRUE;
204            }
205            break;
206
207         case TXa_F2:                           // print full text to SBUF
208            for (s = dat->buf; s && *s; s++)    // all lines in text
209            {
210               length = txSlen( *s);
211               if ((length != 0) || (skp == FALSE))
212               {
213                  TxPrint( "%s\n", *s);
214               }
215               skp = (length == 0);             // skip next line if empty too
216            }
217            break;
218
219         default:
220            txwPostMsg((TXWHANDLE) wnd->owner, TXWM_CHAR, mp1, mp2);
221            break;
222      }
223      if (upd)
224      {
225         rc = txwInvalidateWindow( hwnd, TRUE, TRUE);
226      }
227   }
228   else
229   {
230      rc = TX_INVALID_HANDLE;
231   }
232   RETURN( rc);
233}                                               // end 'txwIkeyTextview'
234/*---------------------------------------------------------------------------*/
235
236/*****************************************************************************/
237// Input key handling for the SBVIEW window class
238/*****************************************************************************/
239ULONG txwIkeySbView                             // RET   result
240(
241   TXWHANDLE           hwnd,                    // IN    current window
242   ULONG               mp1,                     // IN    param 1
243   ULONG               mp2                      // IN    param 2
244)
245{
246   ULONG               rc  = NO_ERROR;
247   TXWINBASE          *wnd = (TXWINBASE *) hwnd;
248   TXWINDOW           *win;
249   BOOL                upd = FALSE;             // window draw required
250   short               size;
251   TXSBVIEW           *dat;
252
253   //- note: sy is the vertical size of the scrollable-area, this does NOT
254   //-       include the bottom line of the display with the status-info
255
256   ENTER();
257   if ((wnd != NULL) && ((win = wnd->window) != NULL))
258   {
259      short            sx    = win->client.right  - win->client.left +1;
260      short            sy    = win->client.bottom - win->client.top;
261
262      dat  = &win->sb;
263      size = (short) dat->sbdata->width;
264      if (dat->sbdata->length > (ULONG) sy)
265      {
266         dat->maxtop = dat->sbdata->length - sy;
267      }
268      else
269      {
270         dat->maxtop = 0;
271      }
272      if (size  > sx)
273      {
274         dat->maxcol = size - sx;
275      }
276      else
277      {
278         dat->maxcol = 0;
279      }
280      TRACES(("top:%4lu, left:%4hu, max:%4lu, lines: %4lu width:%4lu\n",
281               dat->topline, dat->leftcol, dat->maxtop,
282               dat->sbdata->length, dat->sbdata->width));
283      switch (mp2)                              // key value
284      {
285         case TXk_UP:
286         case TXc_UP:
287         case TXa_UP:
288            if (dat->topline != 0)
289            {
290               dat->topline--;
291               upd = TRUE;
292            }
293            break;
294
295         case TXk_DOWN:
296         case TXc_DOWN:
297         case TXa_DOWN:
298            if (dat->topline < dat->maxtop)
299            {
300               dat->topline++;
301               upd = TRUE;
302            }
303            break;
304
305         case TXk_PGUP:
306         case TXc_PGUP:
307         case TXa_PGUP:
308            if (dat->topline > (ULONG) sy)
309            {
310               dat->topline -= sy;
311               upd = TRUE;
312            }
313            else if (dat->topline != 0)
314            {
315               dat->topline = 0;
316               upd = TRUE;
317            }
318            break;
319
320         case TXk_PGDN:
321         case TXc_PGDN:
322         case TXa_PGDN:
323            if (dat->topline + sy <= dat->maxtop)
324            {
325               dat->topline += sy;
326               upd = TRUE;
327            }
328            else if (dat->topline != dat->maxtop)
329            {
330               dat->topline = dat->maxtop;
331               upd = TRUE;
332            }
333            break;
334
335         case TXc_HOME:
336         case TXc_MINUS:
337            if (dat->topline != 0)
338            {
339               if (mp2 == TXc_MINUS)
340               {
341                  dat->topline = 0;
342               }
343               else
344               {
345                  dat->topline = dat->sbdata->firstline;
346               }
347               upd = TRUE;
348            }
349            break;
350
351         case TXc_END:
352            if (dat->topline != dat->maxtop)
353            {
354               dat->topline = dat->maxtop;
355               upd = TRUE;
356            }
357            break;
358
359         case ',':
360         case TXa_COMMA:
361         case TXk_LEFT:
362         case TXc_LEFT:
363         case TXa_LEFT:
364            if (dat->leftcol != 0)
365            {
366               dat->leftcol--;
367               upd = TRUE;
368            }
369            break;
370
371         case '.':
372         case TXa_DOT:
373         case TXk_RIGHT:
374         case TXc_RIGHT:
375         case TXa_RIGHT:
376            if (dat->leftcol < dat->maxcol)
377            {
378               dat->leftcol++;
379               upd = TRUE;
380            }
381            break;
382
383         case TXk_HOME:
384            if (dat->leftcol != 0)
385            {
386               if (dat->leftcol > sx)
387               {
388                  dat->leftcol -= sx;
389               }
390               else
391               {
392                  dat->leftcol = 0;
393               }
394               upd = TRUE;
395            }
396            break;
397
398         case TXk_END:
399            if (dat->leftcol != dat->maxcol)
400            {
401               if (dat->leftcol + sx < dat->maxcol)
402               {
403                  dat->leftcol += sx;
404               }
405               else
406               {
407                  dat->leftcol = dat->maxcol;
408               }
409               upd = TRUE;
410            }
411            break;
412
413         case TXk_ESCAPE:
414         case TXk_F10:                          // Focus to cmdline
415         case TXk_MENU:
416            if (txwa->maincmd != TXHWND_NULL)
417            {
418               if (txwSetFocus(txwa->maincmd) == NO_ERROR)
419               {
420                  TRACES(( "Focus changed to main cmdline window ...\n"));
421                  if (mp2 != TXk_ESCAPE)        // then pass on keystroke
422                  {
423                     txwPostMsg( txwa->maincmd, TXWM_CHAR, mp1, mp2);
424                     TRACES(( "Re-issue (menu) key to cmdline\n"));
425                  }
426               }
427            }
428            break;
429
430         default:
431            txwPostMsg((TXWHANDLE) wnd->owner, TXWM_CHAR, mp1, mp2);
432            break;
433      }
434      if (upd)
435      {
436         rc = txwInvalidateWindow( hwnd, FALSE, TRUE);
437      }
438   }
439   else
440   {
441      rc = TX_INVALID_HANDLE;
442   }
443   RETURN( rc);
444}                                               // end 'txwIkeySbView'
445/*---------------------------------------------------------------------------*/
446
447/*****************************************************************************/
448// Input key handling for the ENTRYFIELD window class
449/*****************************************************************************/
450ULONG txwIkeyEntryfield                         // RET   result
451(
452   TXWHANDLE           hwnd,                    // IN    current window
453   ULONG               mp1,                     // IN    param 1
454   ULONG               mp2                      // IN    param 2
455)
456{
457   ULONG               rc  = NO_ERROR;
458   TXWINBASE          *wnd = (TXWINBASE *) hwnd;
459   TXWINDOW           *win;
460   BOOL                upd = FALSE;             // window draw required
461   short               length;
462   short               index;
463   TXENTRYFIELD       *dat;
464   TXTM                completion;              // completion text
465   char               *cur;
466
467   ENTER();
468   if ((wnd != NULL) && ((win = wnd->window) != NULL))
469   {
470      short            sx    = win->client.right  - win->client.left +1;
471      USHORT           wid   = txwQueryWindowUShort( hwnd, TXQWS_ID);
472
473      dat    = &win->ef;
474      length = txSlen( dat->buf);
475      index  = dat->leftcol + dat->curpos;      // index in string value buffer
476      cur    = &dat->buf[ index];               // address of current char
477      if (length > sx)                          // text wider as window
478      {
479         dat->maxcol = length - sx +1;
480      }
481      else
482      {
483         dat->maxcol = 0;
484      }
485      TRACES(("left:%4lu, max:%4lu, curpos: %4lu\n",
486               dat->leftcol, dat->maxcol, dat->curpos));
487
488      switch (mp2)                              // key value
489      {
490         case TXk_ENTER:
491            txwAdd2History( hwnd, dat->buf);
492            if (win->style & TXES_DLGE_FIELD)   // field in dialog
493            {
494               txwPostMsg((TXWHANDLE) wnd->owner, TXWM_CHAR, (ULONG) wid, mp2);
495            }
496            else                                // stand-alone, clear and upd
497            {
498               dat->curpos  = 0;
499               dat->leftcol = 0;
500               strcpy( dat->buf, "");
501               upd = TRUE;                      // repaint
502            }
503            break;
504
505         case TXc_K:                            // Store in history, clear
506            txwAdd2History( hwnd, dat->buf);
507            strcpy( dat->buf, "");
508            dat->curpos  = 0;
509            dat->leftcol = 0;
510            upd = TRUE;
511            break;
512
513         case TXa_F8:                           // list history (FC2 compat)
514         case TXk_F11:                          // list history buffer
515            if (dat->history != NULL)
516            {
517               TXRECT     where = win->border;  // Close to the field's ULC
518               TXSELIST  *list;
519               ULONG      mcode;                // listbox result
520
521               memset( completion, 0, TXMAXTM);
522               if ((dat->leftcol + dat->curpos) > 0) // prefix there ...
523               {
524                  memcpy( completion, dat->buf, min( dat->leftcol + dat->curpos, TXMAXTM));
525               }
526               if (TxSelistHistory( dat->history, completion, &list) == NO_ERROR)
527               {
528                  list->flags |= TXSL_ITEM_DELETE; //- allow item delete using
529                  mcode = txwListBox(              // Ctrl-D, as ListBox RC
530                           TXHWND_DESKTOP,
531                           hwnd,                // current is owner of popup
532                           &where,              // popup position
533                           "History",           // title in submenu
534                           win->helpid,         // and same global help
535                           TXLB_MOVEABLE,
536                           cMenuTextStand,      // Use Menu color scheme
537                           cMenuBorder_top,     // for client & border
538                           list);
539
540                  switch (mcode)
541                  {
542                     case TXDID_CANCEL:
543                        break;
544
545                     case TXc_D:                // delete current item
546                        if (dat->history != NULL)
547                        {
548                           dat->history->current = list->selected;
549                           txwDelCurrentHistory( dat->history);
550                        }
551                        break;
552
553                     default:
554                        dat->history->current = mcode - TXDID_MAX -1;
555                        strcpy( dat->buf, txwGetHistory( dat->history, TXH_THIS));
556                        break;
557                  }
558                  txSelDestroy( &list);
559
560                  if (mcode == TXc_D)           // deleted item, display popup
561                  {
562                     txwPostMsg( hwnd, TXWM_CHAR, 0, TXk_F11);
563                  }
564                  upd = TRUE;
565               }
566               else
567               {
568                  TxMessage( TRUE, 0, "There are no history items that match the selection criteria");
569               }
570            }
571            break;
572
573         case TXc_D:                            // Delete from history, clear
574            if (dat->history != NULL)
575            {
576               if ((dat->leftcol + dat->curpos) > 0) // prefix there ...
577               {
578                  memset( completion, 0, TXMAXTM);
579                  memcpy( completion, dat->buf, min( dat->leftcol + dat->curpos, TXMAXTM));
580
581                  txwDelCurrentHistory( dat->history);
582
583                  if ((cur = txwRfndHistory( dat->history,
584                                             completion, TRUE)) != NULL)
585                  {
586                     strcpy( dat->buf, cur);
587                  }
588               }
589               else                             // no prefix, take previous
590               {
591                  txwDelCurrentHistory( dat->history);
592                  strcpy( dat->buf, txwGetHistory( dat->history, TXH_PREV));
593               }
594            }
595            upd = TRUE;
596            break;
597
598         case TXk_UP:
599            if (dat->history != NULL)
600            {
601               if ((dat->leftcol + dat->curpos) > 0) // prefix there ...
602               {
603                  memset( completion, 0, TXMAXTM);
604                  memcpy( completion, dat->buf, min( dat->leftcol + dat->curpos, TXMAXTM));
605                  if ((cur = txwRfndHistory( dat->history,
606                                             completion, TRUE)) != NULL)
607                  {
608                     strcpy( dat->buf, cur);
609                  }
610               }
611               else                             // no prefix, take previous
612               {
613                  strcpy( dat->buf, txwGetHistory( dat->history, TXH_PREV));
614               }
615               upd = TRUE;                      // repaint
616            }
617            else
618            {
619               txwSetFocus( txwFindPrevFocus( hwnd, FALSE));
620            }
621            break;
622
623         case TXk_DOWN:
624            if (dat->history != NULL)
625            {
626               if ((dat->leftcol + dat->curpos) > 0) // prefix there ...
627               {
628                  memset( completion, 0, TXMAXTM);
629                  memcpy( completion, dat->buf, min( dat->leftcol + dat->curpos, TXMAXTM));
630                  if ((cur = txwFindHistory( dat->history,
631                                             completion, TRUE)) != NULL)
632                  {
633                     strcpy( dat->buf, cur);
634                     upd = TRUE;                // repaint
635                  }
636               }
637               else                             // no prefix, take next
638               {
639                  strcpy( dat->buf, txwGetHistory( dat->history, TXH_NEXT));
640               }
641               upd = TRUE;                      // repaint
642            }
643            else
644            {
645               txwSetFocus( txwFindNextFocus( hwnd, FALSE));
646            }
647            break;
648
649         case TXk_LEFT:
650            if      (dat->curpos  != 0)         // not at start of field
651            {
652               dat->curpos--;
653               txwSetCursorPos( hwnd, 0, dat->curpos);
654            }
655            else if (dat->leftcol != 0)         // scrolled field
656            {
657               dat->leftcol--;
658               upd = TRUE;                      // repaint
659            }
660            break;
661
662         case TXc_LEFT:                         // one word to left
663            if (index > 0)
664            {
665               index--;
666               cur--;
667               while ((index > 0) && (*cur == ' '))
668               {
669                  index--;
670                  cur--;
671               }
672               while ((index > 0) && (*cur != ' '))
673               {
674                  index--;
675                  cur--;
676               }
677               if (index > 0)                   // stopped at space BEFORE word
678               {
679                  index++;
680                  cur++;
681               }
682               if (index < dat->leftcol)
683               {
684                  dat->leftcol = index;         // curpos at start field
685                  upd = TRUE;                   // repaint
686               }
687               dat->curpos = index - dat->leftcol;
688               txwSetCursorPos( hwnd, 0, dat->curpos);
689            }
690            else                               // related stuff (scroll sbview)
691            {
692               txwPostMsg((TXWHANDLE) wnd->owner, TXWM_CHAR, mp1, mp2);
693            }
694            break;
695
696         case TXk_RIGHT:
697            if (index < dat->rsize -1)          // room to move/grow
698            {
699               if (index >= length)             // beyond current string-value
700               {
701                  strcat( dat->buf, " ");       // extend string by one space
702               }
703               if (dat->curpos  < (sx -1))      // not at end of field
704               {
705                  dat->curpos++;
706                  txwSetCursorPos( hwnd, 0, dat->curpos);
707               }
708               else if (dat->rsize > sx)        // scroll field
709               {
710                  dat->leftcol++;
711                  upd = TRUE;
712               }
713            }
714            else if (dat->rsize == sx)          // no-scrolling field ?
715            {
716               if (dat->buf[0] == ' ')          // string starts with spaces
717               {
718                  memmove( dat->buf, dat->buf +1, txSlen( dat->buf));
719               }
720               upd = TRUE;
721            }
722            break;
723
724         case TXc_RIGHT:                        // one word to right
725            if (index < length -1)              // not at end of string
726            {
727               while ((index < length -1) && (*cur != ' '))
728               {
729                  index++;                      // skip the word itself
730                  cur++;
731               }
732               while ((index < length -1) && (*cur == ' '))
733               {
734                  index++;                      // skip next whitespace
735                  cur++;
736               }
737               if (index > dat->leftcol + sx)
738               {
739                  dat->leftcol = index - sx;
740                  upd = TRUE;
741               }
742               dat->curpos  = index - dat->leftcol;
743               txwSetCursorPos( hwnd, 0, dat->curpos);
744            }
745            else                                // related stuff (scroll sbview)
746            {
747               txwPostMsg((TXWHANDLE) wnd->owner, TXWM_CHAR, mp1, mp2);
748            }
749            break;
750
751         case TXc_E:                            // delete to end of field
752            *cur = '\0';
753            upd = TRUE;                         // repaint
754            break;
755
756         case TXk_ESCAPE:
757            if (win->style & TXES_DLGE_FIELD)   // field in dialog
758            {
759               txwPostMsg((TXWHANDLE) wnd->owner, TXWM_CHAR, mp1, mp2);
760               break;
761            }
762         case TXc_BACKSP:                       // delete whole field