summaryrefslogtreecommitdiffstats
path: root/Python/pythonrun.c
blob: 16aba5c3bcd2e7705ec6412f5ae01f49c54d964d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
/***********************************************************
Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
The Netherlands.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Stichting Mathematisch
Centrum or CWI or Corporation for National Research Initiatives or
CNRI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.

While CWI is the initial source for this software, a modified version
is made available by the Corporation for National Research Initiatives
(CNRI) at the Internet address ftp://ftp.python.org.

STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH
CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

******************************************************************/

/* Python interpreter top-level routines, including init/exit */

#include "Python.h"

#include "grammar.h"
#include "node.h"
#include "parsetok.h"
#include "errcode.h"
#include "compile.h"
#include "eval.h"
#include "marshal.h"

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#ifdef MS_WIN32
#undef BYTE
#include "windows.h"
#endif

extern char *Py_GetPath();

extern grammar _PyParser_Grammar; /* From graminit.c */

/* Forward */
static void initmain Py_PROTO((void));
static void initsite Py_PROTO((void));
static PyObject *run_err_node Py_PROTO((node *n, char *filename,
				   PyObject *globals, PyObject *locals));
static PyObject *run_node Py_PROTO((node *n, char *filename,
			       PyObject *globals, PyObject *locals));
static PyObject *run_pyc_file Py_PROTO((FILE *fp, char *filename,
				   PyObject *globals, PyObject *locals));
static void err_input Py_PROTO((perrdetail *));
static void initsigs Py_PROTO((void));
static void call_sys_exitfunc Py_PROTO((void));
static void call_ll_exitfuncs Py_PROTO((void));

int Py_DebugFlag; /* Needed by parser.c */
int Py_VerboseFlag; /* Needed by import.c */
int Py_InteractiveFlag; /* Needed by Py_FdIsInteractive() below */
int Py_NoSiteFlag; /* Suppress 'import site' */
int Py_UseClassExceptionsFlag = 1; /* Needed by bltinmodule.c */
int Py_FrozenFlag; /* Needed by getpath.c */

static int initialized = 0;

/* API to access the initialized flag -- useful for eroteric use */

int
Py_IsInitialized()
{
	return initialized;
}

/* Global initializations.  Can be undone by Py_Finalize().  Don't
   call this twice without an intervening Py_Finalize() call.  When
   initializations fail, a fatal error is issued and the function does
   not return.  On return, the first thread and interpreter state have
   been created.

   Locking: you must hold the interpreter lock while calling this.
   (If the lock has not yet been initialized, that's equivalent to
   having the lock, but you cannot use multiple threads.)

*/

void
Py_Initialize()
{
	PyInterpreterState *interp;
	PyThreadState *tstate;
	PyObject *bimod, *sysmod;
	char *p;

	if (initialized)
		return;
	initialized = 1;
	
	if ((p = getenv("PYTHONDEBUG")) && *p != '\0')
		Py_DebugFlag = 1;
	if ((p = getenv("PYTHONVERBOSE")) && *p != '\0')
		Py_VerboseFlag = 1;

	interp = PyInterpreterState_New();
	if (interp == NULL)
		Py_FatalError("Py_Initialize: can't make first interpreter");

	tstate = PyThreadState_New(interp);
	if (tstate == NULL)
		Py_FatalError("Py_Initialize: can't make first thread");
	(void) PyThreadState_Swap(tstate);

	interp->modules = PyDict_New();
	if (interp->modules == NULL)
		Py_FatalError("Py_Initialize: can't make modules dictionary");

	bimod = _PyBuiltin_Init_1();
	if (bimod == NULL)
		Py_FatalError("Py_Initialize: can't initialize __builtin__");
	interp->builtins = PyModule_GetDict(bimod);
	Py_INCREF(interp->builtins);

	sysmod = _PySys_Init();
	if (sysmod == NULL)
		Py_FatalError("Py_Initialize: can't initialize sys");
	interp->sysdict = PyModule_GetDict(sysmod);
	Py_INCREF(interp->sysdict);
	_PyImport_FixupExtension("sys", "sys");
	PySys_SetPath(Py_GetPath());
	PyDict_SetItemString(interp->sysdict, "modules",
			     interp->modules);

	/* phase 2 of builtins */
	_PyBuiltin_Init_2(interp->builtins);
	_PyImport_FixupExtension("__builtin__", "__builtin__");

	_PyImport_Init();

	initsigs(); /* Signal handling stuff, including initintr() */

	initmain(); /* Module __main__ */
	if (!Py_NoSiteFlag)
		initsite(); /* Module site */
}

/* Undo the effect of Py_Initialize().

   Beware: if multiple interpreter and/or thread states exist, these
   are not wiped out; only the current thread and interpreter state
   are deleted.  But since everything else is deleted, those other
   interpreter and thread states should no longer be used.

   (XXX We should do better, e.g. wipe out all interpreters and
   threads.)

   Locking: as above.

*/

void
Py_Finalize()
{
	PyInterpreterState *interp;
	PyThreadState *tstate;

	if (!initialized)
		return;
	initialized = 0;

	call_sys_exitfunc();

	/* Get current thread state and interpreter pointer */
	tstate = PyThreadState_Get();
	interp = tstate->interp;

	/* Disable signal handling */
	PyOS_FiniInterrupts();

	/* Destroy PyExc_MemoryErrorInst */
	_PyBuiltin_Fini_1();

	/* Destroy all modules */
	PyImport_Cleanup();

	/* Destroy the database used by _PyImport_{Fixup,Find}Extension */
	_PyImport_Fini();

	/* Debugging stuff */
#ifdef COUNT_ALLOCS
	dump_counts();
#endif

#ifdef Py_REF_DEBUG
	fprintf(stderr, "[%ld refs]\n", _Py_RefTotal);
#endif

#ifdef Py_TRACE_REFS
	if (_Py_AskYesNo("Print left references?")) {
		_Py_PrintReferences(stderr);
	}
#endif /* Py_TRACE_REFS */

	/* Delete current thread */
	PyInterpreterState_Clear(interp);
	PyThreadState_Swap(NULL);
	PyInterpreterState_Delete(interp);

	/* Now we decref the exception classes.  After this point nothing
	   can raise an exception.  That's okay, because each Fini() method
	   below has been checked to make sure no exceptions are ever
	   raised.
	*/
	_PyBuiltin_Fini_2();
	PyMethod_Fini();
	PyFrame_Fini();
	PyCFunction_Fini();
	PyTuple_Fini();
	PyString_Fini();
	PyInt_Fini();
	PyFloat_Fini();

	/* XXX Still allocated:
	   - various static ad-hoc pointers to interned strings
	   - int and float free list blocks
	   - whatever various modules and libraries allocate
	*/

	PyGrammar_RemoveAccelerators(&_PyParser_Grammar);

	call_ll_exitfuncs();

#ifdef Py_TRACE_REFS
	_Py_ResetReferences();
#endif /* Py_TRACE_REFS */
}

/* Create and initialize a new interpreter and thread, and return the
   new thread.  This requires that Py_Initialize() has been called
   first.

   Unsuccessful initialization yields a NULL pointer.  Note that *no*
   exception information is available even in this case -- the
   exception information is held in the thread, and there is no
   thread.

   Locking: as above.

*/

PyThreadState *
Py_NewInterpreter()
{
	PyInterpreterState *interp;
	PyThreadState *tstate, *save_tstate;
	PyObject *bimod, *sysmod;

	if (!initialized)
		Py_FatalError("Py_NewInterpreter: call Py_Initialize first");

	interp = PyInterpreterState_New();
	if (interp == NULL)
		return NULL;

	tstate = PyThreadState_New(interp);
	if (tstate == NULL) {
		PyInterpreterState_Delete(interp);
		return NULL;
	}

	save_tstate = PyThreadState_Swap(tstate);

	/* XXX The following is lax in error checking */

	interp->modules = PyDict_New();

	bimod = _PyImport_FindExtension("__builtin__", "__builtin__");
	if (bimod != NULL) {
		interp->builtins = PyModule_GetDict(bimod);
		Py_INCREF(interp->builtins);
	}
	sysmod = _PyImport_FindExtension("sys", "sys");
	if (bimod != NULL && sysmod != NULL) {
		interp->sysdict = PyModule_GetDict(sysmod);
		Py_INCREF(interp->sysdict);
		PySys_SetPath(Py_GetPath());
		PyDict_SetItemString(interp->sysdict, "modules",
				     interp->modules);
		initmain();
		if (!Py_NoSiteFlag)
			initsite();
	}

	if (!PyErr_Occurred())
		return tstate;

	/* Oops, it didn't work.  Undo it all. */

	PyErr_Print();
	PyThreadState_Clear(tstate);
	PyThreadState_Swap(save_tstate);
	PyThreadState_Delete(tstate);
	PyInterpreterState_Delete(interp);

	return NULL;
}

/* Delete an interpreter and its last thread.  This requires that the
   given thread state is current, that the thread has no remaining
   frames, and that it is its interpreter's only remaining thread.
   It is a fatal error to violate these constraints.

   (Py_Finalize() doesn't have these constraints -- it zaps
   everything, regardless.)

   Locking: as above.

*/

void
Py_EndInterpreter(tstate)
	PyThreadState *tstate;
{
	PyInterpreterState *interp = tstate->interp;

	if (tstate != PyThreadState_Get())
		Py_FatalError("Py_EndInterpreter: thread is not current");
	if (tstate->frame != NULL)
		Py_FatalError("Py_EndInterpreter: thread still has a frame");
	if (tstate != interp->tstate_head || tstate->next != NULL)
		Py_FatalError("Py_EndInterpreter: not the last thread");

	PyImport_Cleanup();
	PyInterpreterState_Clear(interp);
	PyThreadState_Swap(NULL);
	PyInterpreterState_Delete(interp);
}

static char *progname = "python";

void
Py_SetProgramName(pn)
	char *pn;
{
	if (pn && *pn)
		progname = pn;
}

char *
Py_GetProgramName()
{
	return progname;
}

static char *default_home = NULL;

void
Py_SetPythonHome(home)
	char *home;
{
	default_home = home;
}

char *
Py_GetPythonHome()
{
	char *home = default_home;
	if (home == NULL)
		home = getenv("PYTHONHOME");
	return home;
}

/* Create __main__ module */

static void
initmain()
{
	PyObject *m, *d;
	m = PyImport_AddModule("__main__");
	if (m == NULL)
		Py_FatalError("can't create __main__ module");
	d = PyModule_GetDict(m);
	if (PyDict_GetItemString(d, "__builtins__") == NULL) {
		PyObject *bimod = PyImport_ImportModule("__builtin__");
		if (bimod == NULL ||
		    PyDict_SetItemString(d, "__builtins__", bimod) != 0)
			Py_FatalError("can't add __builtins__ to __main__");
	}
}

/* Import the site module (not into __main__ though) */

static void
initsite()
{
	PyObject *m, *f;
	m = PyImport_ImportModule("site");
	if (m == NULL) {
		f = PySys_GetObject("stderr");
		if (Py_VerboseFlag) {
			PyFile_WriteString(
				"'import site' failed; traceback:\n", f);
			PyErr_Print();
		}
		else {
			PyFile_WriteString(
			  "'import site' failed; use -v for traceback\n", f);
			PyErr_Clear();
		}
	}
	else {
		Py_DECREF(m);
	}
}

/* Parse input from a file and execute it */

int
PyRun_AnyFile(fp, filename)
	FILE *fp;
	char *filename;
{
	if (filename == NULL)
		filename = "???";
	if (Py_FdIsInteractive(fp, filename))
		return PyRun_InteractiveLoop(fp, filename);
	else
		return PyRun_SimpleFile(fp, filename);
}

int
PyRun_InteractiveLoop(fp, filename)
	FILE *fp;
	char *filename;
{
	PyObject *v;
	int ret;
	v = PySys_GetObject("ps1");
	if (v == NULL) {
		PySys_SetObject("ps1", v = PyString_FromString(">>> "));
		Py_XDECREF(v);
	}
	v = PySys_GetObject("ps2");
	if (v == NULL) {
		PySys_SetObject("ps2", v = PyString_FromString("... "));
		Py_XDECREF(v);
	}
	for (;;) {
		ret = PyRun_InteractiveOne(fp, filename);
#ifdef Py_REF_DEBUG
		fprintf(stderr, "[%ld refs]\n", _Py_RefTotal);
#endif
		if (ret == E_EOF)
			return 0;
		/*
		if (ret == E_NOMEM)
			return -1;
		*/
	}
}

int
PyRun_InteractiveOne(fp, filename)
	FILE *fp;
	char *filename;
{
	PyObject *m, *d, *v, *w;
	node *n;
	perrdetail err;
	char *ps1 = "", *ps2 = "";
	v = PySys_GetObject("ps1");
	if (v != NULL) {
		v = PyObject_Str(v);
		if (v == NULL)
			PyErr_Clear();
		else if (PyString_Check(v))
			ps1 = PyString_AsString(v);
	}
	w = PySys_GetObject("ps2");
	if (w != NULL) {
		w = PyObject_Str(w);
		if (w == NULL)
			PyErr_Clear();
		else if (PyString_Check(w))
			ps2 = PyString_AsString(w);
	}
	Py_BEGIN_ALLOW_THREADS
	n = PyParser_ParseFile(fp, filename, &_PyParser_Grammar,
			       Py_single_input, ps1, ps2, &err);
	Py_END_ALLOW_THREADS
	Py_XDECREF(v);
	Py_XDECREF(w);
	if (n == NULL) {
		if (err.error == E_EOF) {
			if (err.text)
				free(err.text);
			return E_EOF;
		}
		err_input(&err);
		PyErr_Print();
		return err.error;
	}
	m = PyImport_AddModule("__main__");
	if (m == NULL)
		return -1;
	d = PyModule_GetDict(m);
	v = run_node(n, filename, d, d);
	if (v == NULL) {
		PyErr_Print();
		return -1;
	}
	Py_DECREF(v);
	if (Py_FlushLine())
		PyErr_Clear();
	return 0;
}

int
PyRun_SimpleFile(fp, filename)
	FILE *fp;
	char *filename;
{
	PyObject *m, *d, *v;
	char *ext;

	m = PyImport_AddModule("__main__");
	if (m == NULL)
		return -1;
	d = PyModule_GetDict(m);
	ext = filename + strlen(filename) - 4;
	if (strcmp(ext, ".pyc") == 0 || strcmp(ext, ".pyo") == 0
#ifdef macintosh
	/* On a mac, we also assume a pyc file for types 'PYC ' and 'APPL' */
	    || getfiletype(filename) == 'PYC '
	    || getfiletype(filename) == 'APPL'
#endif /* macintosh */
		) {
		/* Try to run a pyc file. First, re-open in binary */
		/* Don't close, done in main: fclose(fp); */
		if( (fp = fopen(filename, "rb")) == NULL ) {
			fprintf(stderr, "python: Can't reopen .pyc file\n");
			return -1;
		}
		/* Turn on optimization if a .pyo file is given */
		if (strcmp(ext, ".pyo") == 0)
			Py_OptimizeFlag = 1;
		v = run_pyc_file(fp, filename, d, d);
	} else {
		v = PyRun_File(fp, filename, Py_file_input, d, d);
	}
	if (v == NULL) {
		PyErr_Print();
		return -1;
	}
	Py_DECREF(v);
	if (Py_FlushLine())
		PyErr_Clear();
	return 0;
}

int
PyRun_SimpleString(command)
	char *command;
{
	PyObject *m, *d, *v;
	m = PyImport_AddModule("__main__");
	if (m == NULL)
		return -1;
	d = PyModule_GetDict(m);
	v = PyRun_String(command, Py_file_input, d, d);
	if (v == NULL) {
		PyErr_Print();
		return -1;
	}
	Py_DECREF(v);
	if (Py_FlushLine())
		PyErr_Clear();
	return 0;
}

static int
parse_syntax_error(err, message, filename, lineno, offset, text)
     PyObject* err;
     PyObject** message;
     char** filename;
     int* lineno;
     int* offset;
     char** text;
{
	long hold;
	PyObject *v;

	/* old style errors */
	if (PyTuple_Check(err))
		return PyArg_Parse(err, "(O(ziiz))", message, filename,
				   lineno, offset, text);

	/* new style errors.  `err' is an instance */

	if (! (v = PyObject_GetAttrString(err, "msg")))
		goto finally;
	*message = v;

	if (!(v = PyObject_GetAttrString(err, "filename")))
		goto finally;
	if (v == Py_None)
		*filename = NULL;
	else if (! (*filename = PyString_AsString(v)))
		goto finally;

	Py_DECREF(v);
	if (!(v = PyObject_GetAttrString(err, "lineno")))
		goto finally;
	hold = PyInt_AsLong(v);
	Py_DECREF(v);
	v = NULL;
	if (hold < 0 && PyErr_Occurred())
		goto finally;
	*lineno = (int)hold;

	if (!(v = PyObject_GetAttrString(err, "offset")))
		goto finally;
	hold = PyInt_AsLong(v);
	Py_DECREF(v);
	v = NULL;
	if (hold < 0 && PyErr_Occurred())
		goto finally;
	*offset = (int)hold;

	if (!(v = PyObject_GetAttrString(err, "text")))
		goto finally;
	if (v == Py_None)
		*text = NULL;
	else if (! (*text = PyString_AsString(v)))
		goto finally;
	Py_DECREF(v);
	return 1;

finally:
	Py_XDECREF(v);
	return 0;
}

void
PyErr_Print()
{
	PyErr_PrintEx(1);
}

void
PyErr_PrintEx(set_sys_last_vars)
	int set_sys_last_vars;
{
	int err = 0;
	PyObject *exception, *v, *tb, *f;
	PyErr_Fetch(&exception, &v, &tb);
	PyErr_NormalizeException(&exception, &v, &tb);

	if (exception == NULL)
		return;

	if (PyErr_GivenExceptionMatches(exception, PyExc_SystemExit)) {
		if (Py_FlushLine())
			PyErr_Clear();
		fflush(stdout);
		if (v == NULL || v == Py_None)
			Py_Exit(0);
		if (PyInstance_Check(v)) {
			/* we expect the error code to be store in the
			   `code' attribute
			*/
			PyObject *code = PyObject_GetAttrString(v, "code");
			if (code) {
				Py_DECREF(v);
				v = code;
				if (v == Py_None)
					Py_Exit(0);
			}
			/* if we failed to dig out the "code" attribute,
			   then just let the else clause below print the
			   error
			*/
		}
		if (PyInt_Check(v))
			Py_Exit((int)PyInt_AsLong(v));
		else {
			/* OK to use real stderr here */
			PyObject_Print(v, stderr, Py_PRINT_RAW);
			fprintf(stderr, "\n");
			Py_Exit(1);
		}
	}
	if (set_sys_last_vars) {
		PySys_SetObject("last_type", exception);
		PySys_SetObject("last_value", v);
		PySys_SetObject("last_traceback", tb);
	}
	f = PySys_GetObject("stderr");
	if (f == NULL)
		fprintf(stderr, "lost sys.stderr\n");
	else {
		if (Py_FlushLine())
			PyErr_Clear();
		fflush(stdout);
		err = PyTraceBack_Print(tb, f);
		if (err == 0 &&
		    PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError))
		{
			PyObject *message;
			char *filename, *text;
			int lineno, offset;
			if (!parse_syntax_error(v, &message, &filename,
						&lineno, &offset, &text))
				PyErr_Clear();
			else {
				char buf[10];
				PyFile_WriteString("  File \"", f);
				if (filename == NULL)
					PyFile_WriteString("<string>", f);
				else
					PyFile_WriteString(filename, f);
				PyFile_WriteString("\", line ", f);
				sprintf(buf, "%d", lineno);
				PyFile_WriteString(buf, f);
				PyFile_WriteString("\n", f);
				if (text != NULL) {
					char *nl;
					if (offset > 0 &&
					    offset == (int)strlen(text))
						offset--;
					for (;;) {
						nl = strchr(text, '\n');
						if (nl == NULL ||
						    nl-text >= offset)
							break;
						offset -= (nl+1-text);
						text = nl+1;
					}
					while (*text == ' ' || *text == '\t') {
						text++;
						offset--;
					}
					PyFile_WriteString("    ", f);
					PyFile_WriteString(text, f);
					if (*text == '\0' ||
					    text[strlen(text)-1] != '\n')
						PyFile_WriteString("\n", f);
					PyFile_WriteString("    ", f);
					offset--;
					while (offset > 0) {
						PyFile_WriteString(" ", f);
						offset--;
					}
					PyFile_WriteString("^\n", f);
				}
				Py_INCREF(message);
				Py_DECREF(v);
				v = message;
				/* Can't be bothered to check all those
				   PyFile_WriteString() calls */
				if (PyErr_Occurred())
					err = -1;
			}
		}
		if (err) {
			/* Don't do anything else */
		}
		else if (PyClass_Check(exception)) {
			PyClassObject* exc = (PyClassObject*)exception;
			PyObject* className = exc->cl_name;
			PyObject* moduleName =
			      PyDict_GetItemString(exc->cl_dict, "__module__");

			if (moduleName == NULL)
				err = PyFile_WriteString("<unknown>", f);
			else {
				char* modstr = PyString_AsString(moduleName);
				if (modstr && strcmp(modstr, "exceptions")) 
				{
					err = PyFile_WriteString(modstr, f);
					err += PyFile_WriteString(".", f);
				}
			}
			if (err == 0) {
				if (className == NULL)
				      err = PyFile_WriteString("<unknown>", f);
				else
				      err = PyFile_WriteObject(className, f,
							       Py_PRINT_RAW);
			}
		}
		else
			err = PyFile_WriteObject(exception, f, Py_PRINT_RAW);
		if (err == 0) {
			if (v != NULL && v != Py_None) {
				PyObject *s = PyObject_Str(v);
				/* only print colon if the str() of the
				   object is not the empty string
				*/
				if (s == NULL)
					err = -1;
				else if (!PyString_Check(s) ||
					 PyString_GET_SIZE(s) != 0)
					err = PyFile_WriteString(": ", f);
				if (err == 0)
				  err = PyFile_WriteObject(s, f, Py_PRINT_RAW);
				Py_XDECREF(s);
			}
		}
		if (err == 0)
			err = PyFile_WriteString("\n", f);
	}
	Py_XDECREF(exception);
	Py_XDECREF(v);
	Py_XDECREF(tb);
	/* If an error happened here, don't show it.
	   XXX This is wrong, but too many callers rely on this behavior. */
	if (err != 0)
		PyErr_Clear();
}

PyObject *
PyRun_String(str, start, globals, locals)
	char *str;
	int start;
	PyObject *globals, *locals;
{
	return run_err_node(PyParser_SimpleParseString(str, start),
			    "<string>", globals, locals);
}

PyObject *
PyRun_File(fp, filename, start, globals, locals)
	FILE *fp;
	char *filename;
	int start;
	PyObject *globals, *locals;
{
	return run_err_node(PyParser_SimpleParseFile(fp, filename, start),
			    filename, globals, locals);
}

static PyObject *
run_err_node(n, filename, globals, locals)
	node *n;
	char *filename;
	PyObject *globals, *locals;
{
	if (n == NULL)
		return  NULL;
	return run_node(n, filename, globals, locals);
}

static PyObject *
run_node(n, filename, globals, locals)
	node *n;
	char *filename;
	PyObject *globals, *locals;
{
	PyCodeObject *co;
	PyObject *v;
	co = PyNode_Compile(n, filename);
	PyNode_Free(n);
	if (co == NULL)
		return NULL;
	v = PyEval_EvalCode(co, globals, locals);
	Py_DECREF(co);
	return v;
}

static PyObject *
run_pyc_file(fp, filename, globals, locals)
	FILE *fp;
	char *filename;
	PyObject *globals, *locals;
{
	PyCodeObject *co;
	PyObject *v;
	long magic;
	long PyImport_GetMagicNumber();

	magic = PyMarshal_ReadLongFromFile(fp);
	if (magic != PyImport_GetMagicNumber()) {
		PyErr_SetString(PyExc_RuntimeError,
			   "Bad magic number in .pyc file");
		return NULL;
	}
	(void) PyMarshal_ReadLongFromFile(fp);
	v = PyMarshal_ReadObjectFromFile(fp);
	fclose(fp);
	if (v == NULL || !PyCode_Check(v)) {
		Py_XDECREF(v);
		PyErr_SetString(PyExc_RuntimeError,
			   "Bad code object in .pyc file");
		return NULL;
	}
	co = (PyCodeObject *)v;
	v = PyEval_EvalCode(co, globals, locals);
	Py_DECREF(co);
	return v;
}

PyObject *
Py_CompileString(str, filename, start)
	char *str;
	char *filename;
	int start;
{
	node *n;
	PyCodeObject *co;
	n = PyParser_SimpleParseString(str, start);
	if (n == NULL)
		return NULL;
	co = PyNode_Compile(n, filename);
	PyNode_Free(n);
	return (PyObject *)co;
}

/* Simplified interface to parsefile -- return node or set exception */

node *
PyParser_SimpleParseFile(fp, filename, start)
	FILE *fp;
	char *filename;
	int start;
{
	node *n;
	perrdetail err;
	Py_BEGIN_ALLOW_THREADS
	n = PyParser_ParseFile(fp, filename, &_PyParser_Grammar, start,
				(char *)0, (char *)0, &err);
	Py_END_ALLOW_THREADS
	if (n == NULL)
		err_input(&err);
	return n;
}

/* Simplified interface to parsestring -- return node or set exception */

node *
PyParser_SimpleParseString(str, start)
	char *str;
	int start;
{
	node *n;
	perrdetail err;
	n = PyParser_ParseString(str, &_PyParser_Grammar, start, &err);
	if (n == NULL)
		err_input(&err);
	return n;
}

/* Set the error appropriate to the given input error code (see errcode.h) */

static void
err_input(err)
	perrdetail *err;
{
	PyObject *v, *w;
	char *msg = NULL;
	v = Py_BuildValue("(ziiz)", err->filename,
			    err->lineno, err->offset, err->text);
	if (err->text != NULL) {
		free(err->text);
		err->text = NULL;
	}
	switch (err->error) {
	case E_SYNTAX:
		msg = "invalid syntax";
		break;
	case E_TOKEN:
		msg = "invalid token";

		break;
	case E_INTR:
		PyErr_SetNone(PyExc_KeyboardInterrupt);
		return;
	case E_NOMEM:
		PyErr_NoMemory();
		return;
	case E_EOF:
		msg = "unexpected EOF while parsing";
		break;
	case E_INDENT:
		msg = "inconsistent use of tabs and spaces in indentation";
		break;
	default:
		fprintf(stderr, "error=%d\n", err->error);
		msg = "unknown parsing error";
		break;
	}
	w = Py_BuildValue("(sO)", msg, v);
	Py_XDECREF(v);
	PyErr_SetObject(PyExc_SyntaxError, w);
	Py_XDECREF(w);
}

/* Print fatal error message and abort */

void
Py_FatalError(msg)
	char *msg;
{
	fprintf(stderr, "Fatal Python error: %s\n", msg);
#ifdef macintosh
	for (;;);
#endif
#ifdef MS_WIN32
	OutputDebugString("Fatal Python error: ");
	OutputDebugString(msg);
	OutputDebugString("\n");
#endif
	abort();
}

/* Clean up and exit */

#ifdef WITH_THREAD
#include "thread.h"
int _PyThread_Started = 0; /* Set by threadmodule.c and maybe others */
#endif

#define NEXITFUNCS 32
static void (*exitfuncs[NEXITFUNCS])();
static int nexitfuncs = 0;

int Py_AtExit(func)
	void (*func) Py_PROTO((void));
{
	if (nexitfuncs >= NEXITFUNCS)
		return -1;
	exitfuncs[nexitfuncs++] = func;
	return 0;
}

static void
call_sys_exitfunc()
{
	PyObject *exitfunc = PySys_GetObject("exitfunc");

	if (exitfunc) {
		PyObject *res, *f;
		Py_INCREF(exitfunc);
		PySys_SetObject("exitfunc", (PyObject *)NULL);
		f = PySys_GetObject("stderr");
		res = PyEval_CallObject(exitfunc, (PyObject *)NULL);
		if (res == NULL) {
			if (f)
			    PyFile_WriteString("Error in sys.exitfunc:\n", f);
			PyErr_Print();
		}
		Py_DECREF(exitfunc);
	}

	if (Py_FlushLine())
		PyErr_Clear();
}

static void
call_ll_exitfuncs()
{
	while (nexitfuncs > 0)
		(*exitfuncs[--nexitfuncs])();

	fflush(stdout);
	fflush(stderr);
}

#ifdef COUNT_ALLOCS
extern void dump_counts Py_PROTO((void));
#endif

void
Py_Exit(sts)
	int sts;
{
	Py_Finalize();

#ifdef macintosh
	PyMac_Exit(sts);
#else
	exit(sts);
#endif
}

static void
initsigs()
{
#ifdef HAVE_SIGNAL_H
#ifdef SIGPIPE
	signal(SIGPIPE, SIG_IGN);
#endif
#endif /* HAVE_SIGNAL_H */
	PyOS_InitInterrupts(); /* May imply initsignal() */
}

#ifdef Py_TRACE_REFS
/* Ask a yes/no question */

int
_Py_AskYesNo(prompt)
	char *prompt;
{
	char buf[256];
	
	printf("%s [ny] ", prompt);
	if (fgets(buf, sizeof buf, stdin) == NULL)
		return 0;
	return buf[0] == 'y' || buf[0] == 'Y';
}
#endif

#ifdef MPW

/* Check for file descriptor connected to interactive device.
   Pretend that stdin is always interactive, other files never. */

int
isatty(fd)
	int fd;
{
	return fd == fileno(stdin);
}

#endif

/*
 * The file descriptor fd is considered ``interactive'' if either
 *   a) isatty(fd) is TRUE, or
 *   b) the -i flag was given, and the filename associated with
 *      the descriptor is NULL or "<stdin>" or "???".
 */
int
Py_FdIsInteractive(fp, filename)
	FILE *fp;
	char *filename;
{
	if (isatty((int)fileno(fp)))
		return 1;
	if (!Py_InteractiveFlag)
		return 0;
	return (filename == NULL) ||
	       (strcmp(filename, "<stdin>") == 0) ||
	       (strcmp(filename, "???") == 0);
}
="hl kwb">int diff); static int ObjectIsEmpty(Tcl_Obj *objPtr); static char * ComputeSlotAddress(char *recordPtr, int offset); static int PanedWindowIdentifyCoords(PanedWindow *pwPtr, Tcl_Interp *interp, int x, int y); /* * Sashes are between panes only, so there is one less sash than slaves */ #define ValidSashIndex(pwPtr, sash) \ (((sash) >= 0) && ((sash) < ((pwPtr)->numSlaves-1))) static const Tk_GeomMgr panedWindowMgrType = { "panedwindow", /* name */ PanedWindowReqProc, /* requestProc */ PanedWindowLostSlaveProc, /* lostSlaveProc */ }; /* * Information used for objv parsing. */ #define GEOMETRY 0x0001 /* * The following structure contains pointers to functions used for processing * the custom "-sticky" option for slave windows. */ static const Tk_ObjCustomOption stickyOption = { "sticky", /* name */ SetSticky, /* setProc */ GetSticky, /* getProc */ RestoreSticky, /* restoreProc */ NULL, /* freeProc */ 0 }; static const Tk_OptionSpec optionSpecs[] = { {TK_OPTION_BORDER, "-background", "background", "Background", DEF_PANEDWINDOW_BG_COLOR, -1, Tk_Offset(PanedWindow, background), 0, (ClientData) DEF_PANEDWINDOW_BG_MONO, 0}, {TK_OPTION_SYNONYM, "-bd", NULL, NULL, NULL, 0, -1, 0, (ClientData) "-borderwidth", 0}, {TK_OPTION_SYNONYM, "-bg", NULL, NULL, NULL, 0, -1, 0, (ClientData) "-background", 0}, {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_PANEDWINDOW_BORDERWIDTH, -1, Tk_Offset(PanedWindow, borderWidth), 0, 0, GEOMETRY}, {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", DEF_PANEDWINDOW_CURSOR, -1, Tk_Offset(PanedWindow, cursor), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_PIXELS, "-handlepad", "handlePad", "HandlePad", DEF_PANEDWINDOW_HANDLEPAD, -1, Tk_Offset(PanedWindow, handlePad), 0, 0, GEOMETRY}, {TK_OPTION_PIXELS, "-handlesize", "handleSize", "HandleSize", DEF_PANEDWINDOW_HANDLESIZE, Tk_Offset(PanedWindow, handleSizePtr), Tk_Offset(PanedWindow, handleSize), 0, 0, GEOMETRY}, {TK_OPTION_PIXELS, "-height", "height", "Height", DEF_PANEDWINDOW_HEIGHT, Tk_Offset(PanedWindow, heightPtr), Tk_Offset(PanedWindow, height), TK_OPTION_NULL_OK, 0, GEOMETRY}, {TK_OPTION_BOOLEAN, "-opaqueresize", "opaqueResize", "OpaqueResize", DEF_PANEDWINDOW_OPAQUERESIZE, -1, Tk_Offset(PanedWindow, resizeOpaque), 0, 0, 0}, {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", DEF_PANEDWINDOW_ORIENT, -1, Tk_Offset(PanedWindow, orient), 0, (ClientData) orientStrings, GEOMETRY}, {TK_OPTION_RELIEF, "-relief", "relief", "Relief", DEF_PANEDWINDOW_RELIEF, -1, Tk_Offset(PanedWindow, relief), 0, 0, 0}, {TK_OPTION_CURSOR, "-sashcursor", "sashCursor", "Cursor", DEF_PANEDWINDOW_SASHCURSOR, -1, Tk_Offset(PanedWindow, sashCursor), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_PIXELS, "-sashpad", "sashPad", "SashPad", DEF_PANEDWINDOW_SASHPAD, -1, Tk_Offset(PanedWindow, sashPad), 0, 0, GEOMETRY}, {TK_OPTION_RELIEF, "-sashrelief", "sashRelief", "Relief", DEF_PANEDWINDOW_SASHRELIEF, -1, Tk_Offset(PanedWindow, sashRelief), 0, 0, 0}, {TK_OPTION_PIXELS, "-sashwidth", "sashWidth", "Width", DEF_PANEDWINDOW_SASHWIDTH, Tk_Offset(PanedWindow, sashWidthPtr), Tk_Offset(PanedWindow, sashWidth), 0, 0, GEOMETRY}, {TK_OPTION_BOOLEAN, "-showhandle", "showHandle", "ShowHandle", DEF_PANEDWINDOW_SHOWHANDLE, -1, Tk_Offset(PanedWindow, showHandle), 0, 0, GEOMETRY}, {TK_OPTION_PIXELS, "-width", "width", "Width", DEF_PANEDWINDOW_WIDTH, Tk_Offset(PanedWindow, widthPtr), Tk_Offset(PanedWindow, width), TK_OPTION_NULL_OK, 0, GEOMETRY}, {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0} }; static const Tk_OptionSpec slaveOptionSpecs[] = { {TK_OPTION_WINDOW, "-after", NULL, NULL, DEF_PANEDWINDOW_PANE_AFTER, -1, Tk_Offset(Slave, after), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_WINDOW, "-before", NULL, NULL, DEF_PANEDWINDOW_PANE_BEFORE, -1, Tk_Offset(Slave, before), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_PIXELS, "-height", NULL, NULL, DEF_PANEDWINDOW_PANE_HEIGHT, Tk_Offset(Slave, heightPtr), Tk_Offset(Slave, height), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", DEF_PANEDWINDOW_PANE_HIDE, -1, Tk_Offset(Slave, hide), 0,0,GEOMETRY}, {TK_OPTION_PIXELS, "-minsize", NULL, NULL, DEF_PANEDWINDOW_PANE_MINSIZE, -1, Tk_Offset(Slave, minSize), 0, 0, 0}, {TK_OPTION_PIXELS, "-padx", NULL, NULL, DEF_PANEDWINDOW_PANE_PADX, -1, Tk_Offset(Slave, padx), 0, 0, 0}, {TK_OPTION_PIXELS, "-pady", NULL, NULL, DEF_PANEDWINDOW_PANE_PADY, -1, Tk_Offset(Slave, pady), 0, 0, 0}, {TK_OPTION_CUSTOM, "-sticky", NULL, NULL, DEF_PANEDWINDOW_PANE_STICKY, -1, Tk_Offset(Slave, sticky), 0, (ClientData) &stickyOption, 0}, {TK_OPTION_STRING_TABLE, "-stretch", "stretch", "Stretch", DEF_PANEDWINDOW_PANE_STRETCH, -1, Tk_Offset(Slave, stretch), 0, (ClientData) stretchStrings, 0}, {TK_OPTION_PIXELS, "-width", NULL, NULL, DEF_PANEDWINDOW_PANE_WIDTH, Tk_Offset(Slave, widthPtr), Tk_Offset(Slave, width), TK_OPTION_NULL_OK, 0, 0}, {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0} }; /* *-------------------------------------------------------------- * * Tk_PanedWindowObjCmd -- * * This function is invoked to process the "panedwindow" Tcl command. It * creates a new "panedwindow" widget. * * Results: * A standard Tcl result. * * Side effects: * A new widget is created and configured. * *-------------------------------------------------------------- */ int Tk_PanedWindowObjCmd( ClientData clientData, /* NULL. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj * const objv[]) /* Argument objects. */ { PanedWindow *pwPtr; Tk_Window tkwin, parent; OptionTables *pwOpts; XSetWindowAttributes atts; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?"); return TCL_ERROR; } tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), Tcl_GetStringFromObj(objv[1], NULL), NULL); if (tkwin == NULL) { return TCL_ERROR; } pwOpts = (OptionTables *) Tcl_GetAssocData(interp, "PanedWindowOptionTables", NULL); if (pwOpts == NULL) { /* * The first time this function is invoked, the option tables will be * NULL. We then create the option tables from the templates and store * a pointer to the tables as the command's clinical so we'll have * easy access to it in the future. */ pwOpts = ckalloc(sizeof(OptionTables)); /* * Set up an exit handler to free the optionTables struct. */ Tcl_SetAssocData(interp, "PanedWindowOptionTables", DestroyOptionTables, pwOpts); /* * Create the paned window option tables. */ pwOpts->pwOptions = Tk_CreateOptionTable(interp, optionSpecs); pwOpts->slaveOpts = Tk_CreateOptionTable(interp, slaveOptionSpecs); } Tk_SetClass(tkwin, "Panedwindow"); /* * Allocate and initialize the widget record. */ pwPtr = ckalloc(sizeof(PanedWindow)); memset((void *)pwPtr, 0, (sizeof(PanedWindow))); pwPtr->tkwin = tkwin; pwPtr->display = Tk_Display(tkwin); pwPtr->interp = interp; pwPtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(pwPtr->tkwin), PanedWindowWidgetObjCmd, pwPtr, PanedWindowCmdDeletedProc); pwPtr->optionTable = pwOpts->pwOptions; pwPtr->slaveOpts = pwOpts->slaveOpts; pwPtr->relief = TK_RELIEF_RAISED; pwPtr->gc = None; pwPtr->cursor = None; pwPtr->sashCursor = None; /* * Keep a hold of the associated tkwin until we destroy the widget, * otherwise Tk might free it while we still need it. */ Tcl_Preserve(pwPtr->tkwin); if (Tk_InitOptions(interp, (char *) pwPtr, pwOpts->pwOptions, tkwin) != TCL_OK) { Tk_DestroyWindow(pwPtr->tkwin); return TCL_ERROR; } Tk_CreateEventHandler(pwPtr->tkwin, ExposureMask|StructureNotifyMask, PanedWindowEventProc, pwPtr); /* * Find the toplevel ancestor of the panedwindow, and make a proxy win as * a child of that window; this way the proxy can always float above * slaves in the panedwindow. */ parent = Tk_Parent(pwPtr->tkwin); while (!(Tk_IsTopLevel(parent))) { parent = Tk_Parent(parent); if (parent == NULL) { parent = pwPtr->tkwin; break; } } pwPtr->proxywin = Tk_CreateAnonymousWindow(interp, parent, NULL); /* * The proxy window has to be able to share GCs with the main panedwindow * despite being children of windows with potentially different * characteristics, and it looks better that way too. [Bug 702230] Also * set the X window save under attribute to avoid expose events as the * proxy sash is dragged across the panes. [Bug 1036963] */ Tk_SetWindowVisual(pwPtr->proxywin, Tk_Visual(tkwin), Tk_Depth(tkwin), Tk_Colormap(tkwin)); Tk_CreateEventHandler(pwPtr->proxywin, ExposureMask, ProxyWindowEventProc, pwPtr); atts.save_under = True; Tk_ChangeWindowAttributes(pwPtr->proxywin, CWSaveUnder, &atts); if (ConfigurePanedWindow(interp, pwPtr, objc - 2, objv + 2) != TCL_OK) { Tk_DestroyWindow(pwPtr->proxywin); Tk_DestroyWindow(pwPtr->tkwin); return TCL_ERROR; } Tcl_SetObjResult(interp, TkNewWindowObj(pwPtr->tkwin)); return TCL_OK; } /* *-------------------------------------------------------------- * * PanedWindowWidgetObjCmd -- * * This function is invoked to process the Tcl command that corresponds * to a widget managed by this module. See the user documentation for * details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */ static int PanedWindowWidgetObjCmd( ClientData clientData, /* Information about square widget. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj * const objv[]) /* Argument objects. */ { PanedWindow *pwPtr = clientData; int result = TCL_OK; static const char *const optionStrings[] = { "add", "cget", "configure", "forget", "identify", "panecget", "paneconfigure", "panes", "proxy", "sash", NULL }; enum options { PW_ADD, PW_CGET, PW_CONFIGURE, PW_FORGET, PW_IDENTIFY, PW_PANECGET, PW_PANECONFIGURE, PW_PANES, PW_PROXY, PW_SASH }; Tcl_Obj *resultObj; int index, count, i, x, y; Tk_Window tkwin; Slave *slavePtr; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "command", 0, &index) != TCL_OK) { return TCL_ERROR; } Tcl_Preserve(pwPtr); switch ((enum options) index) { case PW_ADD: if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "widget ?widget ...?"); result = TCL_ERROR; break; } result = ConfigureSlaves(pwPtr, interp, objc, objv); break; case PW_CGET: if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); result = TCL_ERROR; break; } resultObj = Tk_GetOptionValue(interp, (char *) pwPtr, pwPtr->optionTable, objv[2], pwPtr->tkwin); if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } break; case PW_CONFIGURE: resultObj = NULL; if (objc <= 3) { resultObj = Tk_GetOptionInfo(interp, (char *) pwPtr, pwPtr->optionTable, (objc == 3) ? objv[2] : NULL, pwPtr->tkwin); if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } } else { result = ConfigurePanedWindow(interp, pwPtr, objc - 2, objv + 2); } break; case PW_FORGET: { int i; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "widget ?widget ...?"); result = TCL_ERROR; break; } /* * Clean up each window named in the arg list. */ for (count = 0, i = 2; i < objc; i++) { Tk_Window slave = Tk_NameToWindow(interp, Tcl_GetString(objv[i]), pwPtr->tkwin); if (slave == NULL) { continue; } slavePtr = GetPane(pwPtr, slave); if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) { count++; Tk_ManageGeometry(slave, NULL, NULL); Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); Tk_DeleteEventHandler(slavePtr->tkwin, StructureNotifyMask, SlaveStructureProc, slavePtr); Tk_UnmapWindow(slavePtr->tkwin); Unlink(slavePtr); } if (count != 0) { ComputeGeometry(pwPtr); } } break; } case PW_IDENTIFY: if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "x y"); result = TCL_ERROR; break; } if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) || (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) { result = TCL_ERROR; break; } result = PanedWindowIdentifyCoords(pwPtr, interp, x, y); break; case PW_PANECGET: if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "pane option"); result = TCL_ERROR; break; } tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), pwPtr->tkwin); if (tkwin == NULL) { result = TCL_ERROR; break; } resultObj = NULL; for (i = 0; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i]->tkwin == tkwin) { resultObj = Tk_GetOptionValue(interp, (char *) pwPtr->slaves[i], pwPtr->slaveOpts, objv[3], tkwin); } } if (i == pwPtr->numSlaves) { Tcl_SetResult(interp, "not managed by this window", TCL_STATIC); } if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } break; case PW_PANECONFIGURE: if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "pane ?option? ?value option value ...?"); result = TCL_ERROR; break; } resultObj = NULL; if (objc <= 4) { tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), pwPtr->tkwin); for (i = 0; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i]->tkwin == tkwin) { resultObj = Tk_GetOptionInfo(interp, (char *) pwPtr->slaves[i], pwPtr->slaveOpts, (objc == 4) ? objv[3] : NULL, pwPtr->tkwin); if (resultObj == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, resultObj); } break; } } } else { result = ConfigureSlaves(pwPtr, interp, objc, objv); } break; case PW_PANES: resultObj = Tcl_NewObj(); Tcl_IncrRefCount(resultObj); for (i = 0; i < pwPtr->numSlaves; i++) { Tcl_ListObjAppendElement(interp, resultObj, Tcl_NewStringObj(Tk_PathName(pwPtr->slaves[i]->tkwin),-1)); } Tcl_SetObjResult(interp, resultObj); Tcl_DecrRefCount(resultObj); break; case PW_PROXY: result = PanedWindowProxyCommand(pwPtr, interp, objc, objv); break; case PW_SASH: result = PanedWindowSashCommand(pwPtr, interp, objc, objv); break; } Tcl_Release(pwPtr); return result; } /* *---------------------------------------------------------------------- * * ConfigureSlaves -- * * Add or alter the configuration options of a slave in a paned window. * * Results: * Standard Tcl result. * * Side effects: * Depends on options; may add a slave to the paned window, may alter the * geometry management options of a slave. * *---------------------------------------------------------------------- */ static int ConfigureSlaves( PanedWindow *pwPtr, /* Information about paned window. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { int i, firstOptionArg, j, found, doubleBw, index, numNewSlaves, haveLoc; int insertIndex; Tk_Window tkwin = NULL, ancestor, parent; Slave *slavePtr, **inserts, **newSlaves; Slave options; const char *arg; /* * Find the non-window name arguments; these are the configure options for * the slaves. Also validate that the window names given are legitimate * (ie, they are real windows, they are not the panedwindow itself, etc.). */ for (i = 2; i < objc; i++) { arg = Tcl_GetString(objv[i]); if (arg[0] == '-') { break; } else { tkwin = Tk_NameToWindow(interp, arg, pwPtr->tkwin); if (tkwin == NULL) { /* * Just a plain old bad window; Tk_NameToWindow filled in an * error message for us. */ return TCL_ERROR; } else if (tkwin == pwPtr->tkwin) { /* * A panedwindow cannot manage itself. */ Tcl_ResetResult(interp); Tcl_AppendResult(interp, "can't add ", arg, " to itself", NULL); return TCL_ERROR; } else if (Tk_IsTopLevel(tkwin)) { /* * A panedwindow cannot manage a toplevel. */ Tcl_ResetResult(interp); Tcl_AppendResult(interp, "can't add toplevel ", arg, " to ", Tk_PathName(pwPtr->tkwin), NULL); return TCL_ERROR; } else { /* * Make sure the panedwindow is the parent of the slave, * or a descendant of the slave's parent. */ parent = Tk_Parent(tkwin); for (ancestor = pwPtr->tkwin;;ancestor = Tk_Parent(ancestor)) { if (ancestor == parent) { break; } if (Tk_IsTopLevel(ancestor)) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "can't add ", arg, " to ", Tk_PathName(pwPtr->tkwin), NULL); return TCL_ERROR; } } } } } firstOptionArg = i; /* * Pre-parse the configuration options, to get the before/after specifiers * into an easy-to-find location (a local variable). Also, check the * return from Tk_SetOptions once, here, so we can save a little bit of * extra testing in the for loop below. */ memset((void *)&options, 0, sizeof(Slave)); if (Tk_SetOptions(interp, (char *) &options, pwPtr->slaveOpts, objc - firstOptionArg, objv + firstOptionArg, pwPtr->tkwin, NULL, NULL) != TCL_OK) { return TCL_ERROR; } /* * If either -after or -before was given, find the numerical index that * corresponds to the given window. If both -after and -before are given, * the option precedence is: -after, then -before. */ index = -1; haveLoc = 0; if (options.after != None) { tkwin = options.after; haveLoc = 1; for (i = 0; i < pwPtr->numSlaves; i++) { if (options.after == pwPtr->slaves[i]->tkwin) { index = i + 1; break; } } } else if (options.before != None) { tkwin = options.before; haveLoc = 1; for (i = 0; i < pwPtr->numSlaves; i++) { if (options.before == pwPtr->slaves[i]->tkwin) { index = i; break; } } } /* * If a window was given for -after/-before, but it's not a window managed * by the panedwindow, throw an error */ if (haveLoc && index == -1) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "window \"", Tk_PathName(tkwin), "\" is not managed by ", Tk_PathName(pwPtr->tkwin), NULL); Tk_FreeConfigOptions((char *) &options, pwPtr->slaveOpts, pwPtr->tkwin); return TCL_ERROR; } /* * Allocate an array to hold, in order, the pointers to the slave * structures corresponding to the windows specified. Some of those * structures may already have existed, some may be new. */ inserts = ckalloc(sizeof(Slave *) * (firstOptionArg - 2)); insertIndex = 0; /* * Populate the inserts array, creating new slave structures as necessary, * applying the options to each structure as we go, and, if necessary, * marking the spot in the original slaves array as empty (for * pre-existing slave structures). */ for (i = 0, numNewSlaves = 0; i < firstOptionArg - 2; i++) { /* * We don't check that tkwin is NULL here, because the pre-pass above * guarantees that the input at this stage is good. */ tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[i + 2]), pwPtr->tkwin); found = 0; for (j = 0; j < pwPtr->numSlaves; j++) { if (pwPtr->slaves[j] != NULL && pwPtr->slaves[j]->tkwin == tkwin) { Tk_SetOptions(interp, (char *) pwPtr->slaves[j], pwPtr->slaveOpts, objc - firstOptionArg, objv + firstOptionArg, pwPtr->tkwin, NULL, NULL); if (pwPtr->slaves[j]->minSize < 0) { pwPtr->slaves[j]->minSize = 0; } found = 1; /* * If the slave is supposed to move, add it to the inserts * array now; otherwise, leave it where it is. */ if (index != -1) { inserts[insertIndex++] = pwPtr->slaves[j]; pwPtr->slaves[j] = NULL; } break; } } if (found) { continue; } /* * Make sure this slave wasn't already put into the inserts array, * i.e., when the user specifies the same window multiple times in a * single add commaned. */ for (j = 0; j < insertIndex; j++) { if (inserts[j]->tkwin == tkwin) { found = 1; break; } } if (found) { continue; } /* * Create a new slave structure and initialize it. All slaves start * out with their "natural" dimensions. */ slavePtr = ckalloc(sizeof(Slave)); memset(slavePtr, 0, sizeof(Slave)); Tk_InitOptions(interp, (char *)slavePtr, pwPtr->slaveOpts, pwPtr->tkwin); Tk_SetOptions(interp, (char *)slavePtr, pwPtr->slaveOpts, objc - firstOptionArg, objv + firstOptionArg, pwPtr->tkwin, NULL, NULL); slavePtr->tkwin = tkwin; slavePtr->masterPtr = pwPtr; doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; if (slavePtr->width > 0) { slavePtr->paneWidth = slavePtr->width; } else { slavePtr->paneWidth = Tk_ReqWidth(tkwin) + doubleBw; } if (slavePtr->height > 0) { slavePtr->paneHeight = slavePtr->height; } else { slavePtr->paneHeight = Tk_ReqHeight(tkwin) + doubleBw; } if (slavePtr->minSize < 0) { slavePtr->minSize = 0; } /* * Set up the geometry management callbacks for this slave. */ Tk_CreateEventHandler(slavePtr->tkwin, StructureNotifyMask, SlaveStructureProc, slavePtr); Tk_ManageGeometry(slavePtr->tkwin, &panedWindowMgrType, slavePtr); inserts[insertIndex++] = slavePtr; numNewSlaves++; } /* * Allocate the new slaves array, then copy the slaves into it, in order. */ i = sizeof(Slave *) * (pwPtr->numSlaves + numNewSlaves); newSlaves = ckalloc(i); memset(newSlaves, 0, (size_t) i); if (index == -1) { /* * If none of the existing slaves have to be moved, just copy the old * and append the new. */ memcpy((void *)&(newSlaves[0]), pwPtr->slaves, sizeof(Slave *) * pwPtr->numSlaves); memcpy((void *)&(newSlaves[pwPtr->numSlaves]), inserts, sizeof(Slave *) * numNewSlaves); } else { /* * If some of the existing slaves were moved, the old slaves array * will be partially populated, with some valid and some invalid * entries. Walk through it, copying valid entries to the new slaves * array as we go; when we get to the insert location for the new * slaves, copy the inserts array over, then finish off the old slaves * array. */ for (i = 0, j = 0; i < index; i++) { if (pwPtr->slaves[i] != NULL) { newSlaves[j] = pwPtr->slaves[i]; j++; } } memcpy((void *)&(newSlaves[j]), inserts, sizeof(Slave *)*insertIndex); j += firstOptionArg - 2; for (i = index; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i] != NULL) { newSlaves[j] = pwPtr->slaves[i]; j++; } } } /* * Make the new slaves array the paned window's slave array, and clean up. */ ckfree(pwPtr->slaves); ckfree(inserts); pwPtr->slaves = newSlaves; /* * Set the paned window's slave count to the new value. */ pwPtr->numSlaves += numNewSlaves; Tk_FreeConfigOptions((char *) &options, pwPtr->slaveOpts, pwPtr->tkwin); ComputeGeometry(pwPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * PanedWindowSashCommand -- * * Implementation of the panedwindow sash subcommand. See the user * documentation for details on what it does. * * Results: * Standard Tcl result. * * Side effects: * Depends on the arguments. * *---------------------------------------------------------------------- */ static int PanedWindowSashCommand( PanedWindow *pwPtr, /* Pointer to paned window information. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const sashOptionStrings[] = { "coord", "dragto", "mark", "place", NULL }; enum sashOptions { SASH_COORD, SASH_DRAGTO, SASH_MARK, SASH_PLACE }; int index, sash, x, y, diff; Tcl_Obj *coords[2]; Slave *slavePtr; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], sashOptionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } Tcl_ResetResult(interp); switch ((enum sashOptions) index) { case SASH_COORD: if (objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "index"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { return TCL_ERROR; } if (!ValidSashIndex(pwPtr, sash)) { Tcl_ResetResult(interp); Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); return TCL_ERROR; } slavePtr = pwPtr->slaves[sash]; coords[0] = Tcl_NewIntObj(slavePtr->sashx); coords[1] = Tcl_NewIntObj(slavePtr->sashy); Tcl_SetObjResult(interp, Tcl_NewListObj(2, coords)); break; case SASH_MARK: if (objc != 6 && objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "index ?x y?"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { return TCL_ERROR; } if (!ValidSashIndex(pwPtr, sash)) { Tcl_ResetResult(interp); Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); return TCL_ERROR; } if (objc == 6) { if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) { return TCL_ERROR; } pwPtr->slaves[sash]->markx = x; pwPtr->slaves[sash]->marky = y; } else { coords[0] = Tcl_NewIntObj(pwPtr->slaves[sash]->markx); coords[1] = Tcl_NewIntObj(pwPtr->slaves[sash]->marky); Tcl_SetObjResult(interp, Tcl_NewListObj(2, coords)); } break; case SASH_DRAGTO: case SASH_PLACE: if (objc != 6) { Tcl_WrongNumArgs(interp, 3, objv, "index x y"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &sash) != TCL_OK) { return TCL_ERROR; } if (!ValidSashIndex(pwPtr, sash)) { Tcl_ResetResult(interp); Tcl_SetResult(interp, "invalid sash index", TCL_STATIC); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) { return TCL_ERROR; } slavePtr = pwPtr->slaves[sash]; if (pwPtr->orient == ORIENT_HORIZONTAL) { if (index == SASH_PLACE) { diff = x - pwPtr->slaves[sash]->sashx; } else { diff = x - pwPtr->slaves[sash]->markx; } } else { if (index == SASH_PLACE) { diff = y - pwPtr->slaves[sash]->sashy; } else { diff = y - pwPtr->slaves[sash]->marky; } } MoveSash(pwPtr, sash, diff); ComputeGeometry(pwPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * ConfigurePanedWindow -- * * This function is called to process an argv/argc list in conjunction * with the Tk option database to configure (or reconfigure) a paned * window widget. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is returned, * then the interp's result contains an error message. * * Side effects: * Configuration information, such as colors, border width, etc. get set * for pwPtr; old resources get freed, if there were any. * *---------------------------------------------------------------------- */ static int ConfigurePanedWindow( Tcl_Interp *interp, /* Used for error reporting. */ PanedWindow *pwPtr, /* Information about widget. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument values. */ { Tk_SavedOptions savedOptions; int typemask = 0; if (Tk_SetOptions(interp, (char *) pwPtr, pwPtr->optionTable, objc, objv, pwPtr->tkwin, &savedOptions, &typemask) != TCL_OK) { Tk_RestoreSavedOptions(&savedOptions); return TCL_ERROR; } Tk_FreeSavedOptions(&savedOptions); PanedWindowWorldChanged(pwPtr); /* * If an option that affects geometry has changed, make a re-layout * request. */ if (typemask & GEOMETRY) { ComputeGeometry(pwPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * PanedWindowWorldChanged -- * * This function is invoked anytime a paned window's world has changed in * some way that causes the widget to have to recompute graphics contexts * and geometry. * * Results: * None. * * Side effects: * Paned window will be relayed out and redisplayed. * *---------------------------------------------------------------------- */ static void PanedWindowWorldChanged( ClientData instanceData) /* Information about the paned window. */ { XGCValues gcValues; GC newGC; PanedWindow *pwPtr = instanceData; /* * Allocated a graphics context for drawing the paned window widget * elements (background, sashes, etc.) and set the window background. */ gcValues.background = Tk_3DBorderColor(pwPtr->background)->pixel; newGC = Tk_GetGC(pwPtr->tkwin, GCBackground, &gcValues); if (pwPtr->gc != None) { Tk_FreeGC(pwPtr->display, pwPtr->gc); } pwPtr->gc = newGC; Tk_SetWindowBackground(pwPtr->tkwin, gcValues.background); /* * Issue geometry size requests to Tk. */ Tk_SetInternalBorder(pwPtr->tkwin, pwPtr->borderWidth); if (pwPtr->width > 0 && pwPtr->height > 0) { Tk_GeometryRequest(pwPtr->tkwin, pwPtr->width, pwPtr->height); } /* * Arrange for the window to be redrawn, if neccessary. */ if (Tk_IsMapped(pwPtr->tkwin) && !(pwPtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayPanedWindow, pwPtr); pwPtr->flags |= REDRAW_PENDING; } } /* *-------------------------------------------------------------- * * PanedWindowEventProc -- * * This function is invoked by the Tk dispatcher for various events on * paned windows. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get cleaned up. When * it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */ static void PanedWindowEventProc( ClientData clientData, /* Information about window. */ XEvent *eventPtr) /* Information about event. */ { PanedWindow *pwPtr = clientData; if (eventPtr->type == Expose) { if (pwPtr->tkwin != NULL && !(pwPtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayPanedWindow, pwPtr); pwPtr->flags |= REDRAW_PENDING; } } else if (eventPtr->type == ConfigureNotify) { pwPtr->flags |= REQUESTED_RELAYOUT; if (pwPtr->tkwin != NULL && !(pwPtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayPanedWindow, pwPtr); pwPtr->flags |= REDRAW_PENDING; } } else if (eventPtr->type == DestroyNotify) { DestroyPanedWindow(pwPtr); } } /* *---------------------------------------------------------------------- * * PanedWindowCmdDeletedProc -- * * This function is invoked when a widget command is deleted. If the * widget isn't already in the process of being destroyed, this command * destroys it. * * Results: * None. * * Side effects: * The widget is destroyed. * *---------------------------------------------------------------------- */ static void PanedWindowCmdDeletedProc( ClientData clientData) /* Pointer to widget record for widget. */ { PanedWindow *pwPtr = clientData; /* * This function could be invoked either because the window was destroyed * and the command was then deleted or because the command was deleted, * and then this function destroys the widget. The WIDGET_DELETED flag * distinguishes these cases. */ if (!(pwPtr->flags & WIDGET_DELETED)) { Tk_DestroyWindow(pwPtr->proxywin); Tk_DestroyWindow(pwPtr->tkwin); } } /* *-------------------------------------------------------------- * * DisplayPanedWindow -- * * This function redraws the contents of a paned window widget. It is * invoked as a do-when-idle handler, so it only runs when there's * nothing else for the application to do. * * Results: * None. * * Side effects: * Information appears on the screen. * *-------------------------------------------------------------- */ static void DisplayPanedWindow( ClientData clientData) /* Information about window. */ { PanedWindow *pwPtr = clientData; Slave *slavePtr; Pixmap pixmap; Tk_Window tkwin = pwPtr->tkwin; int i, sashWidth, sashHeight; const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); pwPtr->flags &= ~REDRAW_PENDING; if ((pwPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } if (pwPtr->flags & REQUESTED_RELAYOUT) { ArrangePanes(clientData); } #ifndef TK_NO_DOUBLE_BUFFERING /* * Create a pixmap for double-buffering, if necessary. */ pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); #else pixmap = Tk_WindowId(tkwin); #endif /* TK_NO_DOUBLE_BUFFERING */ /* * Redraw the widget's background and border. */ Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), pwPtr->borderWidth, pwPtr->relief); /* * Set up boilerplate geometry values for sashes (width, height, common * coordinates). */ if (horizontal) { sashHeight = Tk_Height(tkwin) - (2 * Tk_InternalBorderWidth(tkwin)); sashWidth = pwPtr->sashWidth; } else { sashWidth = Tk_Width(tkwin) - (2 * Tk_InternalBorderWidth(tkwin)); sashHeight = pwPtr->sashWidth; } /* * Draw the sashes. */ for (i = 0; i < pwPtr->numSlaves - 1; i++) { slavePtr = pwPtr->slaves[i]; if (slavePtr->hide) { continue; } if (sashWidth > 0 && sashHeight > 0) { Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, slavePtr->sashx, slavePtr->sashy, sashWidth, sashHeight, 1, pwPtr->sashRelief); } if (pwPtr->showHandle) { Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, slavePtr->handlex, slavePtr->handley, pwPtr->handleSize, pwPtr->handleSize, 1, TK_RELIEF_RAISED); } } #ifndef TK_NO_DOUBLE_BUFFERING /* * Copy the information from the off-screen pixmap onto the screen, then * delete the pixmap. */ XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), pwPtr->gc, 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); Tk_FreePixmap(Tk_Display(tkwin), pixmap); #endif /* TK_NO_DOUBLE_BUFFERING */ } /* *---------------------------------------------------------------------- * * DestroyPanedWindow -- * * This function is invoked by PanedWindowEventProc to free the internal * structure of a paned window. * * Results: * None. * * Side effects: * Everything associated with the paned window is freed up. * *---------------------------------------------------------------------- */ static void DestroyPanedWindow( PanedWindow *pwPtr) /* Info about paned window widget. */ { int i; /* * First mark the widget as in the process of being deleted, so that any * code that causes calls to other paned window functions will abort. */ pwPtr->flags |= WIDGET_DELETED; /* * Cancel idle callbacks for redrawing the widget and for rearranging the * panes. */ if (pwPtr->flags & REDRAW_PENDING) { Tcl_CancelIdleCall(DisplayPanedWindow, pwPtr); } if (pwPtr->flags & RESIZE_PENDING) { Tcl_CancelIdleCall(ArrangePanes, pwPtr); } /* * Clean up the slave list; foreach slave: * o Cancel the slave's structure notification callback * o Cancel geometry management for the slave. * o Free memory for the slave */ for (i = 0; i < pwPtr->numSlaves; i++) { Tk_DeleteEventHandler(pwPtr->slaves[i]->tkwin, StructureNotifyMask, SlaveStructureProc, pwPtr->slaves[i]); Tk_ManageGeometry(pwPtr->slaves[i]->tkwin, NULL, NULL); Tk_FreeConfigOptions((char *) pwPtr->slaves[i], pwPtr->slaveOpts, pwPtr->tkwin); ckfree(pwPtr->slaves[i]); pwPtr->slaves[i] = NULL; } if (pwPtr->slaves) { ckfree(pwPtr->slaves); } /* * Remove the widget command from the interpreter. */ Tcl_DeleteCommandFromToken(pwPtr->interp, pwPtr->widgetCmd); /* * Let Tk_FreeConfigOptions clean up the rest. */ Tk_FreeConfigOptions((char *) pwPtr, pwPtr->optionTable, pwPtr->tkwin); Tcl_Release(pwPtr->tkwin); pwPtr->tkwin = NULL; Tcl_EventuallyFree(pwPtr, TCL_DYNAMIC); } /* *-------------------------------------------------------------- * * PanedWindowReqProc -- * * This function is invoked by Tk_GeometryRequest for windows managed by * a paned window. * * Results: * None. * * Side effects: * Arranges for tkwin, and all its managed siblings, to be re-arranged at * the next idle point. * *-------------------------------------------------------------- */ static void PanedWindowReqProc( ClientData clientData, /* Paned window's information about window * that got new preferred geometry. */ Tk_Window tkwin) /* Other Tk-related information about the * window. */ { Slave *slavePtr = clientData; PanedWindow *pwPtr = (PanedWindow *) slavePtr->masterPtr; if (Tk_IsMapped(pwPtr->tkwin)) { if (!(pwPtr->flags & RESIZE_PENDING)) { pwPtr->flags |= RESIZE_PENDING; Tcl_DoWhenIdle(ArrangePanes, pwPtr); } } else { int doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; if (slavePtr->width <= 0) { slavePtr->paneWidth = Tk_ReqWidth(slavePtr->tkwin) + doubleBw; } if (slavePtr->height <= 0) { slavePtr->paneHeight = Tk_ReqHeight(slavePtr->tkwin) + doubleBw; } ComputeGeometry(pwPtr); } } /* *-------------------------------------------------------------- * * PanedWindowLostSlaveProc -- * * This function is invoked by Tk whenever some other geometry claims * control over a slave that used to be managed by us. * * Results: * None. * * Side effects: * Forgets all information about the slave. Causes geometry to be * recomputed for the panedwindow. * *-------------------------------------------------------------- */ static void PanedWindowLostSlaveProc( ClientData clientData, /* Grid structure for slave window that was * stolen away. */ Tk_Window tkwin) /* Tk's handle for the slave window. */ { register Slave *slavePtr = clientData; PanedWindow *pwPtr = (PanedWindow *) slavePtr->masterPtr; if (pwPtr->tkwin != Tk_Parent(slavePtr->tkwin)) { Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); } Unlink(slavePtr); Tk_DeleteEventHandler(slavePtr->tkwin, StructureNotifyMask, SlaveStructureProc, slavePtr); Tk_UnmapWindow(slavePtr->tkwin); slavePtr->tkwin = NULL; ckfree(slavePtr); ComputeGeometry(pwPtr); } /* *-------------------------------------------------------------- * * ArrangePanes -- * * This function is invoked (using the Tcl_DoWhenIdle mechanism) to * re-layout a set of windows managed by a paned window. It is invoked at * idle time so that a series of pane requests can be merged into a * single layout operation. * * Results: * None. * * Side effects: * The slaves of masterPtr may get resized or moved. * *-------------------------------------------------------------- */ static void ArrangePanes( ClientData clientData) /* Structure describing parent whose slaves * are to be re-layed out. */ { register PanedWindow *pwPtr = clientData; register Slave *slavePtr; int i, slaveWidth, slaveHeight, slaveX, slaveY; int paneWidth, paneHeight, paneSize, paneMinSize; int doubleBw; int x, y; int sashWidth, sashOffset, sashCount, handleOffset; int sashReserve, sxReserve, syReserve; int internalBW; int paneDynSize, paneDynMinSize, pwHeight, pwWidth, pwSize; int first, last; int stretchReserve, stretchAmount; const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); pwPtr->flags &= ~(REQUESTED_RELAYOUT|RESIZE_PENDING); /* * If the parent has no slaves anymore, then don't do anything at all: * just leave the parent's size as-is. Otherwise there is no way to * "relinquish" control over the parent so another geometry manager can * take over. */ if (pwPtr->numSlaves == 0) { return; } Tcl_Preserve(pwPtr); /* * Find index of last visible pane. */ for (i = 0, last = 0, first = -1; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i]->hide == 0) { if (first < 0) { first = i; } last = i; } } /* * First pass; compute sizes */ paneDynSize = paneDynMinSize = 0; internalBW = Tk_InternalBorderWidth(pwPtr->tkwin); pwHeight = Tk_Height(pwPtr->tkwin) - (2 * internalBW); pwWidth = Tk_Width(pwPtr->tkwin) - (2 * internalBW); x = y = internalBW; stretchReserve = (horizontal ? pwWidth : pwHeight); /* * Calculate the sash width, including handle and padding, and the sash * and handle offsets. */ sashOffset = handleOffset = pwPtr->sashPad; if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashWidth = (2 * pwPtr->sashPad) + pwPtr->handleSize; sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2) + pwPtr->sashPad; } else { sashWidth = (2 * pwPtr->sashPad) + pwPtr->sashWidth; handleOffset = ((pwPtr->sashWidth - pwPtr->handleSize) / 2) + pwPtr->sashPad; } for (i = sashCount = 0; i < pwPtr->numSlaves; i++) { slavePtr = pwPtr->slaves[i]; if (slavePtr->hide) { continue; } /* * Compute the total size needed by all the slaves and the left-over, * or shortage of space available. */ if (horizontal) { paneSize = slavePtr->paneWidth; stretchReserve -= paneSize + (2 * slavePtr->padx); } else { paneSize = slavePtr->paneHeight; stretchReserve -= paneSize + (2 * slavePtr->pady); } if (IsStretchable(slavePtr->stretch,i,first,last) && Tk_IsMapped(pwPtr->tkwin)) { paneDynSize += paneSize; paneDynMinSize += slavePtr->minSize; } if (i != last) { stretchReserve -= sashWidth; sashCount++; } } /* * Second pass; adjust/arrange panes. */ for (i = 0; i < pwPtr->numSlaves; i++) { slavePtr = pwPtr->slaves[i]; if (slavePtr->hide) { Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); Tk_UnmapWindow(slavePtr->tkwin); continue; } /* * Compute the size of this slave. The algorithm (assuming a * horizontal paned window) is: * * 1. Get "base" dimensions. If a width or height is specified for * this slave, use those values; else use the ReqWidth/ReqHeight. * 2. Using base dimensions, pane dimensions, and sticky values, * determine the x and y, and actual width and height of the * widget. */ doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; slaveWidth = (slavePtr->width > 0 ? slavePtr->width : Tk_ReqWidth(slavePtr->tkwin) + doubleBw); slaveHeight = (slavePtr->height > 0 ? slavePtr->height : Tk_ReqHeight(slavePtr->tkwin) + doubleBw); paneMinSize = slavePtr->minSize; /* * Calculate pane width and height. */ if (horizontal) { paneSize = slavePtr->paneWidth; pwSize = pwWidth; } else { paneSize = slavePtr->paneHeight; pwSize = pwHeight; } if (IsStretchable(slavePtr->stretch, i, first, last)) { double frac; if (paneDynSize > 0) { frac = (double)paneSize / (double)paneDynSize; } else { frac = (double)paneSize / (double)pwSize; } paneDynSize -= paneSize; paneDynMinSize -= slavePtr->minSize; stretchAmount = (int) (frac * stretchReserve); if (paneSize + stretchAmount >= paneMinSize) { stretchReserve -= stretchAmount; paneSize += stretchAmount; } else { stretchReserve += paneSize - paneMinSize; paneSize = paneMinSize; } if (i == last && stretchReserve > 0) { paneSize += stretchReserve; stretchReserve = 0; } } else if (paneDynSize - paneDynMinSize + stretchReserve < 0) { if (paneSize + paneDynSize - paneDynMinSize + stretchReserve <= paneMinSize) { stretchReserve += paneSize - paneMinSize; paneSize = paneMinSize; } else { paneSize += paneDynSize - paneDynMinSize + stretchReserve; stretchReserve = paneDynMinSize - paneDynSize; } } if (horizontal) { paneWidth = paneSize; paneHeight = pwHeight - (2 * slavePtr->pady); } else { paneWidth = pwWidth - (2 * slavePtr->padx); paneHeight = paneSize; } /* * Adjust for area reserved for sashes. */ if (sashCount) { sashReserve = sashWidth * sashCount; if (horizontal) { sxReserve = sashReserve; syReserve = 0; } else { sxReserve = 0; syReserve = sashReserve; } } else { sxReserve = syReserve = 0; } if (pwWidth - sxReserve < x + paneWidth - internalBW) { paneWidth = pwWidth - sxReserve - x + internalBW; } if (pwHeight - syReserve < y + paneHeight - internalBW) { paneHeight = pwHeight - syReserve - y + internalBW; } if (slaveWidth > paneWidth) { slaveWidth = paneWidth; } if (slaveHeight > paneHeight) { slaveHeight = paneHeight; } slavePtr->x = x; slavePtr->y = y; /* * Compute the location of the sash at the right or bottom of the * parcel and the location of the next parcel. */ if (horizontal) { x += paneWidth + (2 * slavePtr->padx); if (x < internalBW) { x = internalBW; } slavePtr->sashx = x + sashOffset; slavePtr->sashy = y; slavePtr->handlex = x + handleOffset; slavePtr->handley = y + pwPtr->handlePad; x += sashWidth; } else { y += paneHeight + (2 * slavePtr->pady); if (y < internalBW) { y = internalBW; } slavePtr->sashx = x; slavePtr->sashy = y + sashOffset; slavePtr->handlex = x + pwPtr->handlePad; slavePtr->handley = y + handleOffset; y += sashWidth; } /* * Compute the actual dimensions of the slave in the pane. */ slaveX = slavePtr->x; slaveY = slavePtr->y; AdjustForSticky(slavePtr->sticky, paneWidth, paneHeight, &slaveX, &slaveY, &slaveWidth, &slaveHeight); slaveX += slavePtr->padx; slaveY += slavePtr->pady; /* * Now put the window in the proper spot. */ if (slaveWidth <= 0 || slaveHeight <= 0 || (horizontal ? slaveX - internalBW > pwWidth : slaveY - internalBW > pwHeight)) { Tk_UnmaintainGeometry(slavePtr->tkwin, pwPtr->tkwin); Tk_UnmapWindow(slavePtr->tkwin); } else { Tk_MaintainGeometry(slavePtr->tkwin, pwPtr->tkwin, slaveX, slaveY, slaveWidth, slaveHeight); } sashCount--; } Tcl_Release(pwPtr); } /* *---------------------------------------------------------------------- * * Unlink -- * * Remove a slave from a paned window. * * Results: * None. * * Side effects: * The paned window will be scheduled for re-arranging and redrawing. * *---------------------------------------------------------------------- */ static void Unlink( register Slave *slavePtr) /* Window to unlink. */ { register PanedWindow *masterPtr; int i, j; masterPtr = slavePtr->masterPtr; if (masterPtr == NULL) { return; } /* * Find the specified slave in the panedwindow's list of slaves, then * remove it from that list. */ for (i = 0; i < masterPtr->numSlaves; i++) { if (masterPtr->slaves[i] == slavePtr) { for (j = i; j < masterPtr->numSlaves - 1; j++) { masterPtr->slaves[j] = masterPtr->slaves[j + 1]; } break; } } /* * Clean out any -after or -before references to this slave */ for (i = 0; i < masterPtr->numSlaves; i++) { if (masterPtr->slaves[i]->before == slavePtr->tkwin) { masterPtr->slaves[i]->before = None; } if (masterPtr->slaves[i]->after == slavePtr->tkwin) { masterPtr->slaves[i]->after = None; } } masterPtr->flags |= REQUESTED_RELAYOUT; if (!(masterPtr->flags & REDRAW_PENDING)) { masterPtr->flags |= REDRAW_PENDING; Tcl_DoWhenIdle(DisplayPanedWindow, masterPtr); } /* * Set the slave's masterPtr to NULL, so that we can tell that the slave * is no longer attached to any panedwindow. */ slavePtr->masterPtr = NULL; masterPtr->numSlaves--; } /* *---------------------------------------------------------------------- * * GetPane -- * * Given a token to a Tk window, find the pane that corresponds to that * token in a given paned window. * * Results: * Pointer to the slave structure, or NULL if the window is not managed * by this paned window. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Slave * GetPane( PanedWindow *pwPtr, /* Pointer to the paned window info. */ Tk_Window tkwin) /* Window to search for. */ { int i; for (i = 0; i < pwPtr->numSlaves; i++) { if (pwPtr->slaves[i]->tkwin == tkwin) { return pwPtr->slaves[i]; } } return NULL; } /* *-------------------------------------------------------------- * * SlaveStructureProc -- * * This function is invoked whenever StructureNotify events occur for a * window that's managed by a paned window. This function's only purpose * is to clean up when windows are deleted. * * Results: * None. * * Side effects: * The paned window slave structure associated with the window * is freed, and the slave is disassociated from the paned * window which managed it. * *-------------------------------------------------------------- */ static void SlaveStructureProc( ClientData clientData, /* Pointer to record describing window item. */ XEvent *eventPtr) /* Describes what just happened. */ { Slave *slavePtr = clientData; PanedWindow *pwPtr = slavePtr->masterPtr; if (eventPtr->type == DestroyNotify) { Unlink(slavePtr); slavePtr->tkwin = NULL; ckfree(slavePtr); ComputeGeometry(pwPtr); } } /* *---------------------------------------------------------------------- * * ComputeGeometry -- * * Compute geometry for the paned window, including coordinates of all * slave windows and each sash. * * Results: * None. * * Side effects: * Recomputes geometry information for a paned window. * *---------------------------------------------------------------------- */ static void ComputeGeometry( PanedWindow *pwPtr) /* Pointer to the Paned Window structure. */ { int i, x, y, doubleBw, internalBw; int sashWidth, sashOffset, handleOffset; int reqWidth, reqHeight, dim; Slave *slavePtr; const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); pwPtr->flags |= REQUESTED_RELAYOUT; x = y = internalBw = Tk_InternalBorderWidth(pwPtr->tkwin); reqWidth = reqHeight = 0; /* * Sashes and handles share space on the display. To simplify processing * below, precompute the x and y offsets of the handles and sashes within * the space occupied by their combination; later, just add those offsets * blindly (avoiding the extra showHandle, etc, checks). */ sashOffset = handleOffset = pwPtr->sashPad; if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashWidth = (2 * pwPtr->sashPad) + pwPtr->handleSize; sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2) + pwPtr->sashPad; } else { sashWidth = (2 * pwPtr->sashPad) + pwPtr->sashWidth; handleOffset = ((pwPtr->sashWidth - pwPtr->handleSize) / 2) + pwPtr->sashPad; } for (i = 0; i < pwPtr->numSlaves; i++) { slavePtr = pwPtr->slaves[i]; if (slavePtr->hide) { continue; } /* * First set the coordinates for the top left corner of the slave's * parcel. */ slavePtr->x = x; slavePtr->y = y; /* * Make sure the pane's paned dimension is at least minsize. This * check may be redundant, since the only way to change a pane's size * is by moving a sash, and that code checks the minsize. */ if (horizontal) { if (slavePtr->paneWidth < slavePtr->minSize) { slavePtr->paneWidth = slavePtr->minSize; } } else { if (slavePtr->paneHeight < slavePtr->minSize) { slavePtr->paneHeight = slavePtr->minSize; } } /* * Compute the location of the sash at the right or bottom of the * parcel. */ if (horizontal) { x += slavePtr->paneWidth + (2 * slavePtr->padx); slavePtr->sashx = x + sashOffset; slavePtr->sashy = y; slavePtr->handlex = x + handleOffset; slavePtr->handley = y + pwPtr->handlePad; x += sashWidth; } else { y += slavePtr->paneHeight + (2 * slavePtr->pady); slavePtr->sashx = x; slavePtr->sashy = y + sashOffset; slavePtr->handlex = x + pwPtr->handlePad; slavePtr->handley = y + handleOffset; y += sashWidth; } /* * Find the maximum height/width of the slaves, for computing the * requested height/width of the paned window. */ if (horizontal) { /* * If the slave has an explicit height set, use that; otherwise, * use the slave's requested height. */ if (slavePtr->height > 0) { dim = slavePtr->height; } else { doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; dim = Tk_ReqHeight(slavePtr->tkwin) + doubleBw; } dim += 2 * slavePtr->pady; if (dim > reqHeight) { reqHeight = dim; } } else { /* * If the slave has an explicit width set use that; otherwise, use * the slave's requested width. */ if (slavePtr->width > 0) { dim = slavePtr->width; } else { doubleBw = 2 * Tk_Changes(slavePtr->tkwin)->border_width; dim = Tk_ReqWidth(slavePtr->tkwin) + doubleBw; } dim += 2 * slavePtr->padx; if (dim > reqWidth) { reqWidth = dim; } } } /* * The loop above should have left x (or y) equal to the sum of the widths * (or heights) of the widgets, plus the size of one sash and the sash * padding for each widget, plus the width of the left (or top) border of * the paned window. * * The requested width (or height) is therefore x (or y) minus the size of * one sash and padding, plus the width of the right (or bottom) border of * the paned window. * * The height (or width) is equal to the maximum height (or width) of the * slaves, plus the width of the border of the top and bottom (or left and * right) of the paned window. * * If the panedwindow has an explicit width/height set use that; * otherwise, use the requested width/height. */ if (horizontal) { reqWidth = (pwPtr->width > 0 ? pwPtr->width : x - sashWidth + internalBw); reqHeight = (pwPtr->height > 0 ? pwPtr->height : reqHeight + (2 * internalBw)); } else { reqWidth = (pwPtr->width > 0 ? pwPtr->width : reqWidth + (2 * internalBw)); reqHeight = (pwPtr->height > 0 ? pwPtr->height : y - sashWidth + internalBw); } Tk_GeometryRequest(pwPtr->tkwin, reqWidth, reqHeight); if (Tk_IsMapped(pwPtr->tkwin) && !(pwPtr->flags & REDRAW_PENDING)) { pwPtr->flags |= REDRAW_PENDING; Tcl_DoWhenIdle(DisplayPanedWindow, pwPtr); } } /* *---------------------------------------------------------------------- * * DestroyOptionTables -- * * This function is registered as an exit callback when the paned window * command is first called. It cleans up the OptionTables structure * allocated by that command. * * Results: * None. * * Side effects: * Frees memory. * *---------------------------------------------------------------------- */ static void DestroyOptionTables( ClientData clientData, /* Pointer to the OptionTables struct */ Tcl_Interp *interp) /* Pointer to the calling interp */ { ckfree(clientData); } /* *---------------------------------------------------------------------- * * GetSticky - * * Converts an internal boolean combination of "sticky" bits into a Tcl * string obj containing zero or more of n, s, e, or w. * * Results: * Tcl_Obj containing the string representation of the sticky value. * * Side effects: * Creates a new Tcl_Obj. * *---------------------------------------------------------------------- */ static Tcl_Obj * GetSticky( ClientData clientData, Tk_Window tkwin, char *recordPtr, /* Pointer to widget record. */ int internalOffset) /* Offset within *recordPtr containing the * sticky value. */ { int sticky = *(int *)(recordPtr + internalOffset); char buffer[5]; char *p = &buffer[0]; if (sticky & STICK_NORTH) { *p++ = 'n'; } if (sticky & STICK_EAST) { *p++ = 'e'; } if (sticky & STICK_SOUTH) { *p++ = 's'; } if (sticky & STICK_WEST) { *p++ = 'w'; } *p = '\0'; return Tcl_NewStringObj(buffer, -1); } /* *---------------------------------------------------------------------- * * SetSticky -- * * Converts a Tcl_Obj representing a widgets stickyness into an integer * value. * * Results: * Standard Tcl result. * * Side effects: * May store the integer value into the internal representation pointer. * May change the pointer to the Tcl_Obj to NULL to indicate that the * specified string was empty and that is acceptable. * *---------------------------------------------------------------------- */ static int SetSticky( ClientData clientData, Tcl_Interp *interp, /* Current interp; may be used for errors. */ Tk_Window tkwin, /* Window for which option is being set. */ Tcl_Obj **value, /* Pointer to the pointer to the value object. * We use a pointer to the pointer because we * may need to return a value (NULL). */ char *recordPtr, /* Pointer to storage for the widget record. */ int internalOffset, /* Offset within *recordPtr at which the * internal value is to be stored. */ char *oldInternalPtr, /* Pointer to storage for the old value. */ int flags) /* Flags for the option, set Tk_SetOptions. */ { int sticky = 0; char c, *internalPtr; const char *string; internalPtr = ComputeSlotAddress(recordPtr, internalOffset); if (flags & TK_OPTION_NULL_OK && ObjectIsEmpty(*value)) { *value = NULL; } else { /* * Convert the sticky specifier into an integer value. */ string = Tcl_GetString(*value); while ((c = *string++) != '\0') { switch (c) { case 'n': case 'N': sticky |= STICK_NORTH; break; case 'e': case 'E': sticky |= STICK_EAST; break; case 's': case 'S': sticky |= STICK_SOUTH; break; case 'w': case 'W': sticky |= STICK_WEST; break; case ' ': case ',': case '\t': case '\r': case '\n': break; default: Tcl_ResetResult(interp); Tcl_AppendResult(interp, "bad stickyness value \"", Tcl_GetString(*value), "\": must be a string ", "containing zero or more of n, e, s, and w", NULL); return TCL_ERROR; } } } if (internalPtr != NULL) { *((int *) oldInternalPtr) = *((int *) internalPtr); *((int *) internalPtr) = sticky; } return TCL_OK; } /* *---------------------------------------------------------------------- * * RestoreSticky -- * * Restore a sticky option value from a saved value. * * Results: * None. * * Side effects: * Restores the old value. * *---------------------------------------------------------------------- */ static void RestoreSticky( ClientData clientData, Tk_Window tkwin, char *internalPtr, /* Pointer to storage for value. */ char *oldInternalPtr) /* Pointer to old value. */ { *(int *)internalPtr = *(int *)oldInternalPtr; } /* *---------------------------------------------------------------------- * * AdjustForSticky -- * * Given the x,y coords of the top-left corner of a pane, the dimensions * of that pane, and the dimensions of a slave, compute the x,y coords * and actual dimensions of the slave based on the slave's sticky value. * * Results: * No direct return; sets the x, y, slaveWidth and slaveHeight to correct * values. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void AdjustForSticky( int sticky, /* Sticky value; see top of file for * definition. */ int cavityWidth, /* Width of the cavity. */ int cavityHeight, /* Height of the cavity. */ int *xPtr, int *yPtr, /* Initially, coordinates of the top-left * corner of cavity; also return values for * actual x, y coords of slave. */ int *slaveWidthPtr, /* Slave width. */ int *slaveHeightPtr) /* Slave height. */ { int diffx = 0; /* Cavity width - slave width. */ int diffy = 0; /* Cavity hight - slave height. */ if (cavityWidth > *slaveWidthPtr) { diffx = cavityWidth - *slaveWidthPtr; } if (cavityHeight > *slaveHeightPtr) { diffy = cavityHeight - *slaveHeightPtr; } if ((sticky & STICK_EAST) && (sticky & STICK_WEST)) { *slaveWidthPtr += diffx; } if ((sticky & STICK_NORTH) && (sticky & STICK_SOUTH)) { *slaveHeightPtr += diffy; } if (!(sticky & STICK_WEST)) { *xPtr += (sticky & STICK_EAST) ? diffx : diffx/2; } if (!(sticky & STICK_NORTH)) { *yPtr += (sticky & STICK_SOUTH) ? diffy : diffy/2; } } /* *---------------------------------------------------------------------- * * MoveSash -- * * Move the sash given by index the amount given. * * Results: * None. * * Side effects: * Recomputes the sizes of the panes in a panedwindow. * *---------------------------------------------------------------------- */ static void MoveSash( PanedWindow *pwPtr, int sash, int diff) { int i; int expandPane, reduceFirst, reduceLast, reduceIncr, slaveSize, sashOffset; Slave *slavePtr; int stretchReserve = 0; int nextSash = sash + 1; const int horizontal = (pwPtr->orient == ORIENT_HORIZONTAL); if (diff == 0) return; /* * Update the slave sizes with their real sizes. */ if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashOffset = ((pwPtr->handleSize - pwPtr->sashWidth) / 2) + pwPtr->sashPad; } else { sashOffset = pwPtr->sashPad; } for (i = 0; i < pwPtr->numSlaves; i++) { slavePtr = pwPtr->slaves[i]; if (slavePtr->hide) { continue; } if (horizontal) { slavePtr->paneWidth = slavePtr->width = slavePtr->sashx - sashOffset - slavePtr->x - (2 * slavePtr->padx); } else { slavePtr->paneWidth = slavePtr->height = slavePtr->sashy - sashOffset - slavePtr->y - (2 * slavePtr->pady); } } /* * There must be a next sash since it is only possible to enter this * routine when moving an actual sash which implies there exists a visible * pane to either side of the sash. */ while (nextSash < pwPtr->numSlaves-1 && pwPtr->slaves[nextSash]->hide) { nextSash++; } /* * Consolidate +/-diff variables to reduce duplicate code. */ if (diff > 0) { expandPane = sash; reduceFirst = nextSash; reduceLast = pwPtr->numSlaves; reduceIncr = 1; } else { diff = abs(diff); expandPane = nextSash; reduceFirst = sash; reduceLast = -1; reduceIncr = -1; } /* * Calculate how much room we have to stretch in and adjust diff value * accordingly. */ for (i = reduceFirst; i != reduceLast; i += reduceIncr) { slavePtr = pwPtr->slaves[i]; if (slavePtr->hide) { continue; } if (horizontal) { stretchReserve += slavePtr->width - slavePtr->minSize; } else { stretchReserve += slavePtr->height - slavePtr->minSize; } } if (stretchReserve <= 0) { return; } if (diff > stretchReserve) { diff = stretchReserve; } /* * Expand pane by diff amount. */ slavePtr = pwPtr->slaves[expandPane]; if (horizontal) { slavePtr->paneWidth = slavePtr->width += diff; } else { slavePtr->paneHeight = slavePtr->height += diff; } /* * Reduce panes, respecting minsize, until diff amount has been used. */ for (i = reduceFirst; i != reduceLast; i += reduceIncr) { slavePtr = pwPtr->slaves[i]; if (slavePtr->hide) { continue; } if (horizontal) { slaveSize = slavePtr->width; } else { slaveSize = slavePtr->height; } if (diff > (slaveSize - slavePtr->minSize)) { diff -= slaveSize - slavePtr->minSize; slaveSize = slavePtr->minSize; } else { slaveSize -= diff; i = reduceLast - reduceIncr; } if (horizontal) { slavePtr->paneWidth = slavePtr->width = slaveSize; } else { slavePtr->paneHeight = slavePtr->height = slaveSize; } } } /* *---------------------------------------------------------------------- * * ProxyWindowEventProc -- * * This function is invoked by the Tk dispatcher for various events on * paned window proxy windows. * * Results: * None. * * Side effects: * When the window gets deleted, internal structures get cleaned up. Whena * it gets exposed, it is redisplayed. * *-------------------------------------------------------------- */ static void ProxyWindowEventProc( ClientData clientData, /* Information about window. */ XEvent *eventPtr) /* Information about event. */ { PanedWindow *pwPtr = clientData; if (eventPtr->type == Expose) { if (pwPtr->proxywin != NULL &&!(pwPtr->flags & PROXY_REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayProxyWindow, pwPtr); pwPtr->flags |= PROXY_REDRAW_PENDING; } } } /* *-------------------------------------------------------------- * * DisplayProxyWindow -- * * This function redraws a paned window proxy window. It is invoked as a * do-when-idle handler, so it only runs when there's nothing else for * the application to do. * * Results: * None. * * Side effects: * Information appears on the screen. * *-------------------------------------------------------------- */ static void DisplayProxyWindow( ClientData clientData) /* Information about window. */ { PanedWindow *pwPtr = clientData; Pixmap pixmap; Tk_Window tkwin = pwPtr->proxywin; pwPtr->flags &= ~PROXY_REDRAW_PENDING; if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } #ifndef TK_NO_DOUBLE_BUFFERING /* * Create a pixmap for double-buffering, if necessary. */ pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); #else pixmap = Tk_WindowId(tkwin); #endif /* TK_NO_DOUBLE_BUFFERING */ /* * Redraw the widget's background and border. */ Tk_Fill3DRectangle(tkwin, pixmap, pwPtr->background, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 2, pwPtr->sashRelief); #ifndef TK_NO_DOUBLE_BUFFERING /* * Copy the pixmap to the display. */ XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), pwPtr->gc, 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); Tk_FreePixmap(Tk_Display(tkwin), pixmap); #endif /* TK_NO_DOUBLE_BUFFERING */ } /* *---------------------------------------------------------------------- * * PanedWindowProxyCommand -- * * Handles the panedwindow proxy subcommand. See the user documentation * for details. * * Results: * Standard Tcl result. * * Side effects: * May map or unmap the proxy sash. * *---------------------------------------------------------------------- */ static int PanedWindowProxyCommand( PanedWindow *pwPtr, /* Pointer to paned window information. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const optionStrings[] = { "coord", "forget", "place", NULL }; enum options { PROXY_COORD, PROXY_FORGET, PROXY_PLACE }; int index, x, y, sashWidth, sashHeight; Tcl_Obj *coords[2]; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum options) index) { case PROXY_COORD: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } coords[0] = Tcl_NewIntObj(pwPtr->proxyx); coords[1] = Tcl_NewIntObj(pwPtr->proxyy); Tcl_SetObjResult(interp, Tcl_NewListObj(2, coords)); break; case PROXY_FORGET: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } if (Tk_IsMapped(pwPtr->proxywin)) { Tk_UnmapWindow(pwPtr->proxywin); Tk_UnmaintainGeometry(pwPtr->proxywin, pwPtr->tkwin); } break; case PROXY_PLACE: if (objc != 5) { Tcl_WrongNumArgs(interp, 3, objv, "x y"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) { return TCL_ERROR; } if (pwPtr->orient == ORIENT_HORIZONTAL) { if (x < 0) { x = 0; } y = Tk_InternalBorderWidth(pwPtr->tkwin); sashWidth = pwPtr->sashWidth; sashHeight = Tk_Height(pwPtr->tkwin) - (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); } else { if (y < 0) { y = 0; } x = Tk_InternalBorderWidth(pwPtr->tkwin); sashHeight = pwPtr->sashWidth; sashWidth = Tk_Width(pwPtr->tkwin) - (2 * Tk_InternalBorderWidth(pwPtr->tkwin)); } if (sashWidth < 1) { sashWidth = 1; } if (sashHeight < 1) { sashHeight = 1; } /* * Stash the proxy coordinates for future "proxy coord" calls. */ pwPtr->proxyx = x; pwPtr->proxyy = y; /* * Make sure the proxy window is higher in the stacking order than the * slaves, so that it will be visible when drawn. It would be more * correct to push the proxy window just high enough to appear above * the highest slave, but it's much easier to just force it all the * way to the top of the stacking order. */ Tk_RestackWindow(pwPtr->proxywin, Above, NULL); /* * Let Tk_MaintainGeometry take care of placing the window at the * right coordinates. */ Tk_MaintainGeometry(pwPtr->proxywin, pwPtr->tkwin, x, y, sashWidth, sashHeight); break; } return TCL_OK; } /* *---------------------------------------------------------------------- * * ObjectIsEmpty -- * * This function tests whether the string value of an object is empty. * * Results: * The return value is 1 if the string value of objPtr has length zero, * and 0 otherwise. * * Side effects: * May cause object shimmering, since this function can force a * conversion to a string object. * *---------------------------------------------------------------------- */ static int ObjectIsEmpty( Tcl_Obj *objPtr) /* Object to test. May be NULL. */ { int length; if (objPtr == NULL) { return 1; } if (objPtr->bytes != NULL) { return (objPtr->length == 0); } Tcl_GetStringFromObj(objPtr, &length); return (length == 0); } /* *---------------------------------------------------------------------- * * ComputeInternalPointer -- * * Given a pointer to the start of a record and the offset of a slot * within that record, compute the address of that slot. * * Results: * If offset is non-negative, returns the computed address; else, returns * NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char * ComputeSlotAddress( char *recordPtr, /* Pointer to the start of a record. */ int offset) /* Offset of a slot within that record; may be < 0. */ { if (offset >= 0) { return recordPtr + offset; } else { return NULL; } } /* *---------------------------------------------------------------------- * * PanedWindowIdentifyCoords -- * * Given a pair of x,y coordinates, identify the panedwindow component at * that point, if any. * * Results: * Standard Tcl result. * * Side effects: * Modifies the interpreter's result to contain either an empty list, or * a two element list of the form {sash n} or {handle n} to indicate that * the point lies within the n'th sash or handle. * *---------------------------------------------------------------------- */ static int PanedWindowIdentifyCoords( PanedWindow *pwPtr, /* Information about the widget. */ Tcl_Interp *interp, /* Interpreter in which to store result. */ int x, int y) /* Coordinates of the point to identify. */ { Tcl_Obj *list; int i, sashHeight, sashWidth, thisx, thisy; int found, isHandle, lpad, rpad, tpad, bpad; list = Tcl_NewObj(); if (pwPtr->orient == ORIENT_HORIZONTAL) { if (Tk_IsMapped(pwPtr->tkwin)) { sashHeight = Tk_Height(pwPtr->tkwin); } else { sashHeight = Tk_ReqHeight(pwPtr->tkwin); } sashHeight -= 2 * Tk_InternalBorderWidth(pwPtr->tkwin); if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashWidth = pwPtr->handleSize; lpad = (pwPtr->handleSize - pwPtr->sashWidth) / 2; rpad = pwPtr->handleSize - lpad; lpad += pwPtr->sashPad; rpad += pwPtr->sashPad; } else { sashWidth = pwPtr->sashWidth; lpad = rpad = pwPtr->sashPad; } tpad = bpad = 0; } else { if (pwPtr->showHandle && pwPtr->handleSize > pwPtr->sashWidth) { sashHeight = pwPtr->handleSize; tpad = (pwPtr->handleSize - pwPtr->sashWidth) / 2; bpad = pwPtr->handleSize - tpad; tpad += pwPtr->sashPad; bpad += pwPtr->sashPad; } else { sashHeight = pwPtr->sashWidth; tpad = bpad = pwPtr->sashPad; } if (Tk_IsMapped(pwPtr->tkwin)) { sashWidth = Tk_Width(pwPtr->tkwin); } else { sashWidth = Tk_ReqWidth(pwPtr->tkwin); } sashWidth -= 2 * Tk_InternalBorderWidth(pwPtr->tkwin); lpad = rpad = 0; } isHandle = 0; found = -1; for (i = 0; i < pwPtr->numSlaves - 1; i++) { if (pwPtr->slaves[i]->hide) { continue; } thisx = pwPtr->slaves[i]->sashx; thisy = pwPtr->slaves[i]->sashy; if (((thisx - lpad) <= x && x <= (thisx + rpad + sashWidth)) && ((thisy - tpad) <= y && y <= (thisy + bpad + sashHeight))) { found = i; /* * Determine if the point is over the handle or the sash. */ if (pwPtr->showHandle) { thisx = pwPtr->slaves[i]->handlex; thisy = pwPtr->slaves[i]->handley; if (pwPtr->orient == ORIENT_HORIZONTAL) { if (thisy <= y && y <= (thisy + pwPtr->handleSize)) { isHandle = 1; } } else { if (thisx <= x && x <= (thisx + pwPtr->handleSize)) { isHandle = 1; } } } break; } } /* * Set results. */ if (found != -1) { Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj(found)); Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj( (isHandle ? "handle" : "sash"), -1)); } Tcl_SetObjResult(interp, list); return TCL_OK; } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */