summaryrefslogtreecommitdiffstats
path: root/generic/tkMacWinMenu.c
blob: da71310f1b734c1c3735c4585e2eacc3592a02cd (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
/*
 * tkMacWinMenu.c --
 *
 *	This module implements the common elements of the Mac and Windows
 *	specific features of menus. This file is not used for UNIX.
 *
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkMacWinMenu.c,v 1.5 2007/09/07 00:34:53 dgp Exp $
 */

#include "tkInt.h"
#include "tkMenu.h"

typedef struct ThreadSpecificData {
    int postCommandGeneration;
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

static int		PreprocessMenu(TkMenu *menuPtr);

/*
 *----------------------------------------------------------------------
 *
 * PreprocessMenu --
 *
 *	The guts of the preprocessing. Recursive.
 *
 * Results:
 *	The return value is a standard Tcl result (errors can occur while the
 *	postcommands are being processed).
 *
 * Side effects:
 *	Since commands can get executed while this routine is being executed,
 *	the entire world can change.
 *
 *----------------------------------------------------------------------
 */

static int
PreprocessMenu(
    TkMenu *menuPtr)
{
    int index, result, finished;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    Tcl_Preserve((ClientData) menuPtr);

    /*
     * First, let's process the post command on ourselves. If this command
     * destroys this menu, or if there was an error, we are done.
     */

    result = TkPostCommand(menuPtr);
    if ((result != TCL_OK) || (menuPtr->tkwin == NULL)) {
	goto done;
    }

    /*
     * Now, we go through structure and process all of the commands. Since the
     * structure is changing, we stop after we do one command, and start over.
     * When we get through without doing any, we are done.
     */

    do {
	finished = 1;
	for (index = 0; index < menuPtr->numEntries; index++) {
	    register TkMenuEntry *entryPtr = menuPtr->entries[index];

	    if ((entryPtr->type == CASCADE_ENTRY)
		    && (entryPtr->namePtr != NULL)
		    && (entryPtr->childMenuRefPtr != NULL)
		    && (entryPtr->childMenuRefPtr->menuPtr != NULL)) {
		TkMenu *cascadeMenuPtr = entryPtr->childMenuRefPtr->menuPtr;

		if (cascadeMenuPtr->postCommandGeneration !=
			tsdPtr->postCommandGeneration) {
		    cascadeMenuPtr->postCommandGeneration =
			    tsdPtr->postCommandGeneration;
		    result = PreprocessMenu(cascadeMenuPtr);
		    if (result != TCL_OK) {
			goto done;
		    }
		    finished = 0;
		    break;
		}
	    }
	}
    } while (!finished);

  done:
    Tcl_Release((ClientData) menuPtr);
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * TkPreprocessMenu --
 *
 *	On the Mac and on Windows, all of the postcommand processing has to be
 *	done on the entire tree underneath the main window to be posted. This
 *	means that we have to traverse the menu tree and issue the
 *	postcommands for all of the menus that have cascades attached. Since
 *	the postcommands can change the menu structure while we are
 *	traversing, we have to be extremely careful. Basically, the idea is to
 *	traverse the structure until we succesfully process one postcommand.
 *	Then we start over, and do it again until we traverse the whole
 *	structure without processing any postcommands.
 *
 *	We are also going to set up the cascade back pointers in here since we
 *	have to traverse the entire structure underneath the menu anyway. We
 *	can clear the postcommand marks while we do that.
 *
 * Results:
 *	The return value is a standard Tcl result (errors can occur while the
 *	postcommands are being processed).
 *
 * Side effects:
 *	Since commands can get executed while this routine is being executed,
 *	the entire world can change.
 *
 *----------------------------------------------------------------------
 */

int
TkPreprocessMenu(
    TkMenu *menuPtr)
{
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    tsdPtr->postCommandGeneration++;
    menuPtr->postCommandGeneration = tsdPtr->postCommandGeneration;
    return PreprocessMenu(menuPtr);
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */