Initial git import
[sxemacs] / src / EmacsShell-sub.c
1 /* Emacs shell widget -- define the two widgets.
2    Copyright (C) 1994, 1995 Sun Microsystems, Inc.
3
4 This file is part of SXEmacs
5
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19
20 /* Synched up with: Not in FSF. */
21
22 /* Written by Ben Wing, May, 1994. */
23
24 /*
25    It is rather unfortunate that we have to do this.  Blame those
26    short-sighted people who designed the monstrosities known as
27    Xt and ICCCM.
28 */
29
30 /*
31    This widget is not actually Emacs-specific; perhaps there could
32    be a better name than "EmacsShell".  What it does is work around
33    a limitation in Xt in correctly dealing with the window-manager
34    size hints with applications that
35
36    (a) dynamically change their window size
37    (b) have a cell size (width-inc and height-inc) other than 1
38
39    and
40
41    (c) cannot predict in advance exactly what size their shell will be
42        (This is the more common situation, when you have a number
43        of widgets, each with their own size ideas)
44
45    This widget assumes that your program contains a fixed "base size"
46    plus some number of cells (e.g. character cells).  The WMShell
47    resources "widthInc" and "heightInc" specify the size of a
48    character cell, and the window manager will report the app's
49    size in cells rather than in pixels.
50
51    If you use this widget, do not use the WMShell resources
52    "baseWidth", "baseHeight", "minWidth", or "minHeight".
53    Instead, use "widthCells" and "heightCells" to specify the
54    current size in cells (you must keep this up-to-date),
55    and "minWidthCells" and "minHeightCells" to specify the
56    minimum size in cells.
57
58    Every time that the program issues a size command, the
59    "baseWidth", "baseHeight", "minWidth", and "minHeight" fields
60    of the WM_NORMAL_HINTS property will be updated to stay in
61    line with the resource values specified above.  The calculations
62    are done once the desired shell size is known but before the
63    window-manager size-change request is issued. (We must do it
64    at this time because before then we don't know what size we
65    will request, and after the request the deed has already
66    been done.)
67
68    After you change the "baseWidth", "baseHeight", "minWidth",
69    or "minHeight" resources, you need to call
70    EmacsShellUpdateSizeHints() to manually update the size
71    hints, except in the following two circumstances:
72
73    (a) you are about to make a geometry request.
74    (b) you are changing only "baseWidth" and "baseHeight"
75        from within a resize procedure.  (In this case,
76        the size hints are already correct.)
77
78 */
79
80 #include <config.h>
81
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <X11/StringDefs.h>
85 #include "xintrinsicp.h"
86 #include <X11/Shell.h>
87 #include <X11/ShellP.h>
88 #include <X11/Vendor.h>
89 #include <X11/VendorP.h>
90 #include "EmacsShellP.h"
91
92 #if defined (DEFINE_TOP_LEVEL_EMACS_SHELL)
93 #define EMACS_SHELL_WIDGET TopLevelEmacsShellWidget
94 #define SUPERCLASS_WIDGET_CLASS topLevelShellWidgetClass
95 #define SUPERCLASS_CLASS_REC topLevelShellClassRec
96 #define EMACS_SHELL_REC TopLevelEmacsShellRec
97 #define EMACS_SHELL_CLASS_REC topLevelEmacsShellClassRec
98 #define EMACS_SHELL_CLASS_REC_TYPE TopLevelEmacsShellClassRec
99 #define EMACS_SHELL_CLASS_NAME "TopLevelEmacsShell"
100 #define EMACS_SHELL_WIDGET_CLASS topLevelEmacsShellWidgetClass
101 #define EMACS_SHELL_UPDATE_SIZE_HINTS TopLevelEmacsShellUpdateSizeHints
102 #elif defined (DEFINE_TRANSIENT_EMACS_SHELL)
103 #define EMACS_SHELL_WIDGET TransientEmacsShellWidget
104 #define SUPERCLASS_WIDGET_CLASS transientShellWidgetClass
105 #define SUPERCLASS_CLASS_REC transientShellClassRec
106 #define EMACS_SHELL_REC TransientEmacsShellRec
107 #define EMACS_SHELL_CLASS_REC transientEmacsShellClassRec
108 #define EMACS_SHELL_CLASS_REC_TYPE TransientEmacsShellClassRec
109 #define EMACS_SHELL_CLASS_NAME "TransientEmacsShell"
110 #define EMACS_SHELL_WIDGET_CLASS transientEmacsShellWidgetClass
111 #define EMACS_SHELL_UPDATE_SIZE_HINTS TransientEmacsShellUpdateSizeHints
112 #else
113 Error.Must define either DEFINE_TOP_LEVEL_EMACS_SHELL or
114     DEFINE_TRANSIENT_EMACS_SHELL.
115 #endif
116     typedef struct {
117         XtPointer next_extension;
118         XrmQuark record_type;
119         long version;
120         Cardinal record_size;
121 } GenericClassExtRec;
122
123 static XtGeometryResult RootGeometryManager(Widget gw,
124                                             XtWidgetGeometry * request,
125                                             XtWidgetGeometry * reply);
126 static void ChangeManaged(Widget w);
127
128 /* snarfed from Shell.c */
129 #define BIGSIZE ((Dimension)32767)
130
131 static XtResource resources[] = {
132 #define offset(field) XtOffset(EMACS_SHELL_WIDGET, emacs_shell.field)
133 #define coreoffset(field) XtOffset(EMACS_SHELL_WIDGET, core.field)
134 #ifdef LWLIB_USES_MOTIF
135         /* *** BOGOSITY^10! *** The Motif VendorShell fucks around with
136            the default values for X and Y, for no obvious reason.  This
137            causes Shell to indicate that the defaults of (0,0) were
138            program-specified, instead of letting the WM do what it wants. */
139         {XtNx, XtCPosition,
140          XtRPosition, sizeof(Position),
141          coreoffset(x), XtRImmediate, (XtPointer) BIGSIZE}
142         ,
143         {XtNy, XtCPosition,
144          XtRPosition, sizeof(Position),
145          coreoffset(y), XtRImmediate, (XtPointer) BIGSIZE}
146         ,
147 #endif
148         {XtNwidthCells, XtCWidthCells,
149          XtRInt, sizeof(int),
150          offset(width_cells), XtRImmediate, (XtPointer) 0},
151         {XtNheightCells, XtCHeightCells,
152          XtRInt, sizeof(int),
153          offset(height_cells), XtRImmediate, (XtPointer) 0},
154         {XtNminWidthCells, XtCMinWidthCells,
155          XtRInt, sizeof(int),
156          offset(min_width_cells), XtRImmediate, (XtPointer) 0},
157         {XtNminHeightCells, XtCMinHeightCells,
158          XtRInt, sizeof(int),
159          offset(min_height_cells), XtRImmediate, (XtPointer) 0},
160 };
161
162 static CompositeClassExtensionRec compositeClassExtRec = {
163         NULL,
164         NULLQUARK,
165         XtCompositeExtensionVersion,
166         sizeof(CompositeClassExtensionRec),
167         TRUE,
168 };
169
170 static ShellClassExtensionRec shellClassExtRec = {
171         NULL,
172         NULLQUARK,
173         XtShellExtensionVersion,
174         sizeof(ShellClassExtensionRec),
175         RootGeometryManager
176 };
177
178 EMACS_SHELL_CLASS_REC_TYPE EMACS_SHELL_CLASS_REC = {
179         {                       /*
180                                  *        core_class fields
181                                  */
182          /* superclass         */ (WidgetClass) & SUPERCLASS_CLASS_REC,
183          /* class_name         */ (String) EMACS_SHELL_CLASS_NAME,
184          /* size               */ sizeof(EMACS_SHELL_REC),
185          /* Class Initializer  */ NULL,
186                                                 /* class_part_initialize */ NULL,
187                                                 /* XtInheritClassPartInitialize, */
188          /* Class init'ed ?    */ FALSE,
189          /* initialize         */ NULL,
190          /* initialize_notify  */ NULL,
191          /* realize            */ XtInheritRealize,
192          /* actions            */ NULL,
193          /* num_actions        */ 0,
194          /* resources          */ resources,
195          /* resource_count     */ XtNumber(resources),
196          /* xrm_class          */ NULLQUARK,
197          /* compress_motion    */ TRUE,
198          /* compress_exposure  */ XtExposeCompressMaximal | XtExposeNoRegion,
199          /* compress_enterleave */ TRUE,
200          /* visible_interest   */ TRUE,
201          /* destroy            */ NULL,
202          /* resize             */ XtInheritResize,
203          /* expose             */ NULL,
204                                         /* set_values         */ NULL,
205                                         /* XtInheritSetValues, */
206          /* set_values_hook    */ NULL,
207          /* set_values_almost  */ XtInheritSetValuesAlmost,
208          /* get_values_hook    */ NULL,
209          /* accept_focus       */ NULL,
210          /* intrinsics version */ XtVersion,
211          /* callback offsets   */ NULL,
212          /* tm_table           */ NULL,
213          /* query_geometry     */ NULL,
214          /* display_accelerator */ NULL,
215          /* extension          */ NULL
216          }
217         , {                     /* Composite */
218            /* geometry_manager   */ XtInheritGeometryManager,
219            /* change_managed     */ ChangeManaged,
220            /* insert_child       */ XtInheritInsertChild,
221            /* delete_child       */ XtInheritDeleteChild,
222            /* extension          */ (XtPointer) & compositeClassExtRec
223            }
224         , {                     /* Shell */
225            /* extension          */ (XtPointer) & shellClassExtRec
226            }
227         , {                     /* WMShell */
228            /* extension          */ NULL
229            }
230         , {                     /* VendorShell */
231            /* extension          */ NULL
232            }
233         , {                     /* TopLevelShell or TransientShell */
234            /* both have exactly one XtPointer here. */
235            /* extension          */ NULL
236            }
237         , {                     /* EmacsShell */
238            0}
239 };
240
241 WidgetClass EMACS_SHELL_WIDGET_CLASS = (WidgetClass) & EMACS_SHELL_CLASS_REC;
242
243 static void
244 update_size_hints_internal(EMACS_SHELL_WIDGET w, int width, int height)
245 {
246         int base_width, base_height;
247         int cell_width, cell_height;
248         Arg al[10];
249
250         /* time to update them thar size hints */
251         cell_width = w->wm.size_hints.width_inc;
252         cell_height = w->wm.size_hints.height_inc;
253         base_width = width - cell_width * w->emacs_shell.width_cells;
254         base_height = height - cell_height * w->emacs_shell.height_cells;
255 #ifdef DEBUG_GEOMETRY_MANAGEMENT
256         /* Very useful info when debugging geometry management problems.
257            When it's guaranteed that no more such problems exist, take
258            this stuff out. */
259         printf("update_size_hints_internal:\n");
260         printf("  actual pixel size: %d %d\n", width, height);
261         printf("  cell size in pixels: %d %d\n", cell_width, cell_height);
262         printf("  text area size in cells: %d %d\n", w->emacs_shell.width_cells,
263                w->emacs_shell.height_cells);
264         printf("  base size set to: %d %d\n", base_width, base_height);
265         fflush(stdout);
266 #endif
267         XtSetArg(al[0], XtNbaseWidth, base_width);
268         XtSetArg(al[1], XtNbaseHeight, base_height);
269         XtSetArg(al[2], XtNminWidth, base_width +
270                  cell_width * w->emacs_shell.min_width_cells);
271         XtSetArg(al[3], XtNminHeight, base_height +
272                  cell_height * w->emacs_shell.min_height_cells);
273         XtSetValues((Widget) w, al, 4);
274 }
275
276 static XtGeometryResult
277 SuperClassRootGeometryManager(Widget gw,
278                               XtWidgetGeometry * request,
279                               XtWidgetGeometry * reply)
280 {
281         ShellWidgetClass swc = (ShellWidgetClass) SUPERCLASS_WIDGET_CLASS;
282         ShellClassExtensionRec *scer;
283         GenericClassExtRec *gcer;
284
285         /* find the shell extension record that specifies the
286            root geometry manager method */
287         for (gcer = (GenericClassExtRec *) swc->shell_class.extension;
288              gcer; gcer = (GenericClassExtRec *) gcer->next_extension) {
289                 if (gcer->record_type == NULLQUARK)
290                         break;
291         }
292
293         if (!gcer)
294                 abort();
295
296         /* call it to actually make the geometry request */
297         scer = (ShellClassExtensionRec *) gcer;
298         return (scer->root_geometry_manager) (gw, request, reply);
299 }
300
301 static XtGeometryResult
302 RootGeometryManager(Widget gw,
303                     XtWidgetGeometry * request, XtWidgetGeometry * reply)
304 {
305         EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) gw;
306         /* OK since this file is not dumped */
307         static int reentrant = 0;
308         XtGeometryResult result;
309
310         if (reentrant)
311                 abort();
312         reentrant++;
313
314 #ifdef DEBUG_GEOMETRY_MANAGEMENT
315         printf("root_geometry_manager:\n");
316         printf("  current shell size: %d %d\n", w->core.width, w->core.height);
317         if (request->request_mode & CWWidth)
318                 printf("width requested;");
319         if (request->request_mode & CWHeight)
320                 printf("height requested;");
321         printf("\n");
322         printf("  requested shell size: %d %d\n", request->width,
323                request->height);
324 #endif
325         /* update the size hints */
326         update_size_hints_internal(w,
327                                    request->request_mode & CWWidth ?
328                                    request->width : w->core.width,
329                                    request->request_mode & CWHeight ?
330                                    request->height : w->core.height);
331
332         result = SuperClassRootGeometryManager(gw, request, reply);
333
334 #ifdef DEBUG_GEOMETRY_MANAGEMENT
335         printf("  result: %s\n",
336                result == XtGeometryYes ? "XtGeometryYes" :
337                result == XtGeometryNo ? "XtGeometryNo" :
338                result == XtGeometryAlmost ? "XtGeometryAlmost" :
339                "XtGeometryDone");
340         if (reply->request_mode & CWWidth)
341                 printf("width returned;");
342         if (reply->request_mode & CWHeight)
343                 printf("height returned;");
344         printf("\n");
345         printf("  resulting shell size: %d %d\n", reply->width, reply->height);
346         printf("----------\n");
347         fflush(stdout);
348 #endif
349         reentrant--;
350         return result;
351 }
352
353 static void ChangeManaged(Widget wid)
354 {
355         EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) wid;
356
357         /* If not realized, then we're being called from XtRealizeWidget().
358            RootGeometryManager() has not yet been called, and thus our
359            base size is incorrect.  We need to set it now or the Shell
360            will mess up geometry specifications with negative positional
361            offsets. */
362         if (!XtIsRealized(wid)) {
363                 Widget child = NULL;
364                 Cardinal i;
365
366                 /* the managed child indicates what our size is */
367                 for (i = 0; i < w->composite.num_children; i++) {
368                         if (XtIsManaged(w->composite.children[i])) {
369                                 child = w->composite.children[i];       
370                                 update_size_hints_internal(w, child->core.width,
371                                                            child->core.height);
372                         break;
373                         }
374                 }
375
376         }
377
378         /* call the real ChangeManaged */
379         (((ShellWidgetClass) SUPERCLASS_WIDGET_CLASS)->
380          composite_class.change_managed) (wid);
381 }
382
383 /******************* external entry points *********************/
384
385 void EMACS_SHELL_UPDATE_SIZE_HINTS(Widget gw)
386 {
387         EMACS_SHELL_WIDGET w = (EMACS_SHELL_WIDGET) gw;
388         update_size_hints_internal(w, w->core.width, w->core.height);
389 }