Research in Computer Science II Spring 2010

A feasibility study for a general-purpose visual programming system

Dimitar Asenov

Supervisor: Peter M¨uller

19th July, 2010 Contents

1 Introduction and Motivation 4

2 Related work 5 2.1 Visual programming languages ...... 5 2.2 Visualization techniques and interaction tools ...... 6 2.3 Visualization evaluation and experiments ...... 7

3 The Envision system 8 3.1 Overview ...... 8 3.2 Source code representation ...... 9 3.2.1 Visualization principles of Envision ...... 10 General-purpose ...... 10 Efficient ...... 10 Fast ...... 10 Intuitive ...... 10 Flexible ...... 10 Focused ...... 11 Appropriate ...... 11 Self-sufficient ...... 11 Varied ...... 11 Friendly ...... 11 3.2.2 Entity visualizations ...... 11 3.3 User interaction ...... 13 3.3.1 Construction of visuals ...... 13 3.3.2 Giving commands ...... 15 3.4 Use case examples ...... 15 3.4.1 Programming a Hello World application ...... 15 3.4.2 Visualizing the source code of Envision itself ...... 18 3.5 Case study: call stack visualization ...... 19

4 Discussion 19 4.1 Implementation of the visualization principles ...... 20 4.1.1 General-purpose ...... 20 4.1.2 Efficient ...... 20 4.1.3 Fast ...... 20 4.1.4 Intuitive ...... 21

2 4.1.5 Flexible ...... 21 4.1.6 Focused ...... 21 4.1.7 Appropriate ...... 21 4.1.8 Self-sufficient ...... 21 4.1.9 Varied ...... 21 4.1.10 Friendly ...... 22 4.2 Comparison to traditional programming environments ...... 22 4.2.1 Productivity ...... 22 4.2.2 Readability ...... 22 4.2.3 Navigation ...... 23 4.2.4 Maintainability (Viscosity) ...... 23 4.2.5 Teaching ...... 23

5 Future work 23

6 Conclusion 24

3 Abstract

As electronics and computers become an ever-bigger part of our life there is a growing need for creating higher-quality software faster. Visualizing the structure, execution and model of programs can improve the development process. Numerous methods and styles exist for visualizing different software artifacts but, despite previous efforts, visual programming has only very limited use in industrial organizations. We present Envision - a framework for general-purpose visual programming. It combines many existing techniques with novel ideas to increase productivity and code comprehension. It aims to support visual programming for large scale industrial projects in a wide variety of domains.

1 Introduction and Motivation

Throughout the history of computer science many new programming languages have been de- veloped. This is hardly surprising given the fact that computational devices are playing an ever greater role in our life. The increasing variety and power of computers present a challenge to exist- ing software solutions. To better manage the complexity of growing applications it is necessary to develop new ways to program. There can be many driving goals when creating a new language: to make it easier to learn how to program, to introduce support for a new or hardware feature, to make it very efficient, etc. However, languages alone can no longer cope with large-scale development tasks. To alleviate the problem there is a continuing effort to improve the integrated development environments (IDEs) that provide a platform around a language to make it easier to create and navigate a big programming project. During all these developments one thing has remained constant - programming languages are textual. The programmer creates a program by writing its source code. In the process, different program structures and modules are saved to different files or folders. The source code, written using a precise grammar, fully defines the structure and behavior of the program. This poses a number of important restrictions:

• The developer must learn and use a very specific language syntax. This often includes rules which are not intuitive for novice programmers. Even experienced programmers might find it difficult or cumbersome to use these rules (e.g. when writing scripts in the Bash scripting language). • Creating new programing elements and navigating existing ones relies mostly on symbolic reasoning and memory. • Additional resources required for developing bigger projects such as documentation, dia- grams, etc. are separate from the source code. They help the programmer write the applica- tion, but do not have a direct influence on it and are not linked to program elements. This causes inconsistencies when only the code or the documentation is modified.

• Getting familiar with a larger software base by only looking at its source code is an over- whelming task. To make this possible one refers to the supporting documentation, since the source code itself does not guide the programmer in how to explore the application.

While modern IDEs help to reduce the impact of these restrictions they can not remove them since these restrictions are inherent to textual representations. In an effort to truly remove some of these drawbacks researches have experimented with visual programming languages. Some of the advantages that they found when using visual representations are:

4 • Visual objects can more directly map to programming entities, removing the need for com- plicated syntax. Thus it is easier for novice programmers to learn to use such a language. Experienced programmers can also benefit from this and be more productive. • Visual programming involves not only symbolic, but also visual and spatial cognitive pro- cesses. This can improve both the comprehension and retention of programs as the graphical representations more closely match a programmer’s mental model. Studies [1, 2] have shown that retention for visual images is better compared to that for text. • Two dimensional graphical representations can be more expressive compared to text which uses a single dimension. • Figures, diagrams, tables and such, can be directly embedded in a visual program and make it easier to understand and explore.

Intuitively visual representations can help better organize a programming activity. Despite previous efforts to develop visual languages, the term programming is still commonly associated with writing source code. The top 20 languages used in industry in June 2010 accord- ing to the popular ranking site TIBOE 1 are all textual. Other on-line rankings for languages yield similar results. While the initial hopes for visual languages were high, they did not meet researchers’ expectations. Meanwhile research focus has shifted away from visual programming to software visualization and end-user programming. We propose a new take on visual programming. Learning from the lessons of previous research and developing for today’s powerful hardware we have established the concept of Envision - a general-purpose visual programming system. The goals we set to achieve include developing a highly flexible visual programming environment, that easily scales to large programming projects while improving the developer’s experience and performance compared to modern IDEs. This paper reports on an initial study designed to asses the feasibility of our approach. Section 2 explores related work in the fields of visual languages, software visualization and human- computer interaction. Next, the main ideas behind the visualization and user interaction in Envision are presented in section 3. The subsequent section 4 assesses the performance of our approach compared to existing programming techniques. In section 5 we report on our plans for further development of the system. Finally we summarize our findings in section 6.

2 Related work

2.1 Visual programming languages

Many visual programming languages (VPLs) have been developed in the past. Schiffer and Fr¨ohlich designed Vista[3] - a VPL which builds on top of the object-oriented programming (OOP) paradigm and aims to provide support for high-level building blocks and ease of use. At the more abstract level, programming in Vista involves building entities and networks between components in a visual environment. A central notion in the language is the processor which is a logical unit in the program that has a distinct function. Processors are somewhat similar to classes and communicate with each other via data and signal tokens. Different processors can be connected to each other using a graphical user interface (GUI). In the GUI each processor is represented by an icon. At the lower level of abstraction Vista provides access to the full Smalltalk library and allows writing code fragments using text. Another tool for visual programming is Seity[4]. It is a visual environment for the Self . Seity represents objects as three-dimensional boxes that list an object’s properties and

1http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

5 methods. An important concept for the environment is the focus on objects, rather than views. Each object is unique and appears at most once on the screen but an object can present different views of itself based on the current needs of the programmer. Chang and his colleagues argue that this makes it easier for the programmer to maintain object identity which is not the case with view-focused IDEs. They propose that manipulating the object directly and not through an intermediate view, makes the objects concrete and tangible. On the other hand using tools such as class browsers or object inspectors makes the objects seem secondary and distant. Citrin et al. in [5] propose VIPR - an entirely new approach to programming based on visually nesting circles. A program in VIPR would be represented by a circle that consists of a series of nested circles, representing statements, classes, methods, etc. The execution proceeds by perform- ing the statement connected with the outer-most circle and removing that circle. More complicated execution traces can be created by connecting different circles with arrows which indicate the next statement and possibly have an associated condition. An advantage of VIPR is that each state of a program at run-time is itself a valid VIPR program - the same language is used for programming and execution visualization. Another entirely visual approach to writing object-oriented programs is using Prograph[6]. Classes in Prograph are represented by icons and so are each class’ fields and methods. A method is defined visually by connecting various types of entities together using arrows. Entities include inputs, outputs, function calls, constants, variables, object instances, etc. Prograph comes with a fully featured environment that allows the user to explore the program using various views. This includes structure visualizations, class diagrams, property explorers and others. More recently Grant has designed Visula[7]. The key idea behind Visula is the use of UML-like sequence diagrams to define program behavior. The diagrams are extended with control-flow and class definition elements. Since UML-diagrams are used by many professionals such an approach has the benefit of being familiar to experts. Most VPLs are short-lived and rarely get much attention outside of research circles. One notable exception is National Instruments’ LabView. It is a visual data-flow language specifically designed for data acquisition, analysis and instrument control and automation. Applications represent in- struments. Each application has a virtual front panel where the user can interact with a wide variety of controls: buttons, knobs, displays, light emitting diodes etc. The logic of the applica- tion is defined by its back panel where different processing elements can be connected to define the data flow. A processing element can be an entire instrument, a built-in library function, an arithmetic or string operation, etc. Flow control entities such as loops and conditionals are also included. Programming in LabView is mostly done using the mouse, by dragging and dropping components from a palette onto the instrument panel and connecting them using wires. In [8], Baroth and Hartsough report on their experience with the language at the Jet Propulsion Labo- ratory, California Institute of Technology under a contract with the US National Aeronautics and Space Administration. The same software system was developed simultaneously in both LabView and C. The two teams were given identical budget and deadlines to come up with a solution. The team using LabView managed to cover and significantly exceed the requirements of the software while maintaining constant contact with the customer. The team using C did not further com- municate with the customer after receiving the initial requirements and did not make a product that fully meets them. Our personal experience with LabView in designing automated tests is also very positive. However LabView is not very well suited for general purpose programming. In particular its aim at data-flow programming makes it harder to model more reactive systems. Its support for OOP principles is also limited.

2.2 Visualization techniques and interaction tools

Although the hope had been high that VPLs would bring a revolution in programming, this was not the case. Nevertheless developers realized that visualizations can indeed improve the

6 software development process. Thus many tools were created which instead of substituting textual programming, complement it with visual representations. They have enjoyed a much higher success and adoption rate in industrial environments. Major efforts in visualization tools are often implemented in new versions of popular IDEs or as IDE extensions. Modern programming environments come with color coded language syntax, auto-completion tools, code snippet libraries, UML diagram generators, etc. The Human Interactions in Programming 2 group at Microsoft Corporation continuously eval- uates new ways to improve the usability of Microsoft’s Visual Studio IDE. Among others they have investigated the effect of presenting code usage information directly in the IDE to improve navigation[9, 10]; why and how software developers use graphics and whether these needs can be met by embedding images directly in the IDE[11]; improving navigation by visualizing code with automatically generated maps[12] or displaying code-thumbnails of files[13]. Being an open-source tool with a strong community and industrial support the Eclipse3 IDE has also enjoyed a lot of attention from researchers. One of the more popular extensions developed for Eclipse is Shrimp[14]. It can visualize various aspects of Java code including class hierarchies, packages or individual classes. Additionally it has support for showing associated documentation and navigating around the different elements. Going one step further is CodeBubbles[15, 16] by Bragdon et al. This tool uses Eclipse only as a back-end and presents the user’s code in a new environment that features a visual representation of code based on a bubbles metaphor. A class, a method, a JavaDoc snippet or even custom notes can be displayed in an independent frame - the bubble. Each bubble is a text editor in itself. Many bubbles can be shown on the screen and the programmer can arrange them in any way she prefers. This helps with task oriented programming activities such as debugging and maintenance. Another interesting extension for Eclipse, that focuses on user input is Quack[17]. It allows the user to program ”sloppily” by entering keywords instead of proper statements. Quack would then try its best to determine the programmer’s true intent and create the corresponding statements.

2.3 Visualization evaluation and experiments

Whitely has compiled an overview of VPLs in [18]. It is immediately evident that there are only a few quantitative experiments that asses the viability of visual programming. Whitley concludes that no program representation (textual or visual) is better in absolute terms. Some representations yield better results in specific tasks, while other representations in others. It is important that the task at hand and the chosen representation match well. Further observations were that for smaller programs text might be a preferred option and that special attention should be paid to how well screen estate is utilized by visual representations. In the end visuals are said to improve the programming experience but there are not enough experimental studies or sufficient cognitive theories to guide the design of VPLs. The classic Cognitive Dimensions(CD) framework by Green and Petre[19] has been often used to evaluate the design of a new VPL. Their work suggests a number of criteria by which a program- ming language and its environment can be evaluated. Initially they applied their framework to three languages: FORTRAN, Prograph and LabView. They found that generally the construc- tion of visual programs is easier because there are less syntactic hassles and because of the use of higher-level operators. They also identified several problems with visual programming that need to be addressed: clutter, insufficient screen real-estate and high viscosity (resistance to local change of programs).

2http://research.microsoft.com/en-us/groups/hip/ 3http://www.eclipse.org/

7 3 The Envision system

The purpose of this feasibility study is to introduce and evaluate the visualization and user inter- action concepts of Envision. However the overall plan for the Envision system goes beyond this. To provide a better insight of the context of this study we will first present the full set of features planned for Envision. Afterwards we elaborate on the visualization and interaction parts of the system including demonstrative examples.

3.1 Overview

The concept for Envision is a combination of an IDE and an information system. Like a typical IDE Envision allows programmers to create software and aids in this process in various ways. Acting as an information system Envision maintains the software’s repository, including all supporting documentation, graphs, tables etc. and keeps these development artifacts in a consistent state. Here is a list of the most distinguishing features in both domains: IDE:

• Visual source code representation - the programmer does not work purely with text, but rather interacts with a visual representation of the program. The visualization also includes text in order to achieve maximum flexibility. • Command enabled user interaction - apart from standard means of input, the user can also issue commands to the system in a style similar to an shell. These com- mands are dependent on the currently selected program entity and can represent arbitrary operations such as constructing new program elements, conducting searches, invoking a new process or running system commands.

Information system:

• Semantic versioning - unlike traditional file-based version control systems Envision stores versions of programming entities like classes, methods, fields etc. This allows for better tracking of software evolution and enables new analysis techniques.

• Content linking - a program will not be just the instructions it executes but will also con- tain all supporting development artifacts. Figures, animations, tables or documents can be embedded directly in the software project. Moreover links can exist between different types of content to keep the application in a consistent state. For example the value of a constant can be taken from a table in the document describing its meaning and computation.

• Continuous analysis - the Envision system will continuously analyze the software in the back- ground. This involves activities such as automatic test-case execution, formal verification and collection of statistics. This information will be reported to the user directly embedded in the visual representation of the software to aid with the development process.

Envision is primarily aimed at large-scale software development. While we also consider beginners, the main target users are expert programmers. The system is meant to be a general purpose IDE that can be used to efficiently build a wide variety of software. To this end it should also support the more popular programming paradigms. In developing the concept for Envision we strive to maintain a positive answer to these questions:

• Would we want to use this system every day for our coding needs? • Can we efficiently build different kinds of software with it?

8 • Could we use it to create large-scale software products such as an operating system? • Does the system feel familiar and intuitive for experienced programmers? • Is it fun and easy to work with?

Our hypothesis is that the combination of these features in an integrated system is very powerful. The work in this feasibility study represents the first step towards testing our designs. Once the system is more mature we plan to conduct quantitative studies to objectively evaluate the fulfillment of the above criteria. It is important to note that at the moment Envision is still in design phase and only a few features were implemented as part of this study. These are discussed next.

3.2 Source code representation

Despite the large number of visualization techniques developed by researches there is a lack of quantitative experiments that allow us to set rules for what are good software visualizations and what are not[18]. A few works exist that try to establish some guiding principles for creating visual languages. Here we will briefly discuss two that have helped guide our effort. Green and Petre developed the classical Cognitive Dimensions framework[19]. It provides re- searches with a means to evaluate their designs. Originally the framework was applied to one textual and two visual programming languages. In our work we have applied Green and Petre’s five conclusions regarding future work in VPL design:

1. Reduced syntactic load and higher-level operators. Building a program visually reduces the need to remember and use precise syntax rules common for textual languages. Visual source- code representations allow for higher-level operations to be expressed concisely. These fea- tures increase the concentration of the programmer. 2. Improved secondary notation. The capabilities of modern graphics hardware have come a long way since the Cognitive Dimensions framework was conceived. Envision uses a broad spectrum of visual capabilities in order to improve secondary notation in programs. This includes colors, shapes, shadows, translucency, visual effects, visual transformations, icons, animations, grouping, modularizing and others. 3. Familiar control-flow representation. In Envision control flow statements have received a visual overhaul but remain structurally close to what programmers are used to in traditional languages.

4. Low viscosity. Unlike many previous efforts to design a VPL, an explicit goal for Envision is to be at least as productive as text-based environments with respect to writing or modifying code. We hope to achieve this by relying heavily on keyboard based input for the IDE. The input mechanisms of Envision are discussed in detail in section 3.3. 5. Better utilization of screen real-estate. Although it is quite a different concept Envision resembles CodeBubbles[15, 16] when it comes to how the screen can be utilized to show software fragments. CodeBubbles has been shown to increase the number of function code visible on the screen by about 50% in the typical case. We hope to achieve similar results.

More recently, Petre has analyzed four previous studies on the mental imagery that expert pro- grammers use[20]. She also summarizes lessons learned about characteristics of good visualiza- tions. Her work has helped shape the guiding principles(3.2.1) of the visualization component in Envision.

9 3.2.1 Visualization principles of Envision

Here we list the major design principles that have shaped the initial software representations of Envision. We also provide the rationale behind them. Software visualization in Envision should be . . .

General-purpose Envision is meant to be employed in a wide variety of domains. The visual- ization component should match this goal.

• Many domain-specific VPLs exist and some also enjoy success in industrial settings[21, 22, 23]. However, for general-purpose applications, purely textual languages like Java, C#, C, C++, Visual Basic, PHP, Python and their text-based IDEs dominate the market. We want to promote visual programming in this segment.

Efficient The system should allow the programmer to be at least as effective in constructing software as text-based IDEs.

• There is little incentive to switch to a new type of IDE if it does not provide tangible benefits. We feel that past efforts to introduce visual programming have often failed to deliver enough with respect to programmer productivity. One of the core reasons for developing Envision is to offer a platform that ultimately improves the programming experience.

Fast The system should be highly responsive to the user.

• Computing a complicated visual scene has the potential to reduce the computer’s respon- siveness to user input. This is not acceptable for Envision which wants to compete with text-based IDEs where there is no such problem. The performance of modern graphics hardware should be utilized to deliver an optimal user experience.

Intuitive The programmer should find it easy to use our environment. Visualizations should look familiar and perform as expected.

• Two important aspects hinder the adoption of new tools - unfamiliarity and unconventional behavior. More often than not, past VPLs were not successful in penetrating the commercial market because they required completely new programming paradigms. Consequently any benefits they offered were outweighed by the complexity of learning how to use or interpret them. Envision is based on OOP - a programming paradigm that has stood the test of time and has proven to be exceptionally versatile. A programmer should be able to work with the system with a minimal introduction. Where appropriate Envision might provide hints to the user of how to use novel features.

Flexible The visuals in the system should be flexible to display as much detail or as higher- level of abstraction as needed. Their flexibility should at a minimum match the one of textual representations. It should also be possible to create new views and tools through plug-ins.

• In their every day job programmers face a variety of tasks. A good tool should be able to provide the necessary level of abstraction to quickly navigate and understand software. If a tool is too narrow in its selection of views or detail level it will, at best, only be used for specific purposes. This is unacceptable for a general-purpose IDE targeting large-scale development projects.

10 Focused Visual support should be focused on the task at hand and on the immediate needs of the programmer. Unnecessary tools or options should not be visible.

• In popular IDEs such as Eclipse or Microsoft Visual Studio there are a lot of different tools, buttons and menus visible by default. For the most time however a programmer does not need them. Showing those tools on the screen contributes little to the programming task at hand when they are not needed. Instead screen real-estate can be better utilized by displaying more of what the user is actually working on - be it a software fragment or a document.

Appropriate The representations of software entities should match the nature of the represented data.

• One of the major outcomes of Whitley’s analysis of empirical studies about VPLs[18] is that visuals perform poorly when they are not suited to the entity they represent. It is vital to carefully choose what visualizations to use in which cases. Equally important is to be able to say no to a graphic when a few simple lines of text feel much more natural. Envision combines visuals with text in an effort to deliver an optimal experience for various programming elements.

Self-sufficient No other tool should be needed to design large-scale software. In particular no stand-alone textual editors should be used.

• Envision should provide enough support and levels of abstraction to cover the entire range of developer needs. Even accessing more complicated features such as memory alignment declarations or special CPU instructions should be achievable from within the IDE.

Varied The looks of the IDE should take advantage of modern hardware and offer a wide variety of visual cues and styles.

• Limited by hardware performance the VPLs of one or two decades ago could not look as appealing and polished as it is possible nowadays. It is essential to exploit the full capabil- ities of modern hardware to deliver a fully immersing programming experience. Secondary notations would also benefit from this.

Friendly Envision should be fun to use. It should just ’feel right’.

• If the programmer thinks a tool is hard to use he or she will find alternatives to replace it. This is especially true of a new IDE that someone is considering for future use. Envision should be characterized by a natural and inviting interface. Only then will it be considered for serious software development.

3.2.2 Entity visualizations

On the higher abstraction level the visualization of software in Envision is based on boxes or blocks. These are used to display entities such as applications, modules, classes and methods. More detailed levels of software fragments such as method statements or class fields are specified in textual format. Figure 1 shows a basic hello world application. The outer-most box is the application. It contains modules which are similar to packages in Java. In this case there is

11 HelloApp

Main HelloApp

text string

main

text = "Hello World!" print(text)

Figure 1: A Hello World application. only one module - Main. The module’s color and the position of the module boxes within the application is determined by the programmer. A module contains classes. The Hello World application has only one class - HelloApp. Classes are indicated by an icon resembling a class hierarchy. The background color of each class is slightly transparent so that a user working on a bigger class can identify the module it belongs to. The user determines the position of a class within the enclosing module. A class contains fields and methods. Figure 2 shows the representation of a simple circle class. The box in the upper left corner of the class shows defined fields. They are split into three categories in three columns - from left to right: public, protected and private. The Circle class has a boolean field filled which is public and three private fields: x, y and radius. Methods within a class are also contained in boxes. These boxes have a white background to improve readability and can be manually placed anywhere in the class. The Circle class has two methods. A method is indicated by an icon depicting two gears and its name. Furthermore on the right of the name a box with the method arguments appears. There the type of an argument appears under its name.HelloA pp method contains a sequence of statements. As we can see from the area method these statements are not limited to plain text but can include special symbols, graphics, formatting, effects,Ma etc.in Currently Envision only supports textual statements with a few exceptions (discussed below). We plan to extend this further. For example special symbols and

Circle

filled boolean x int y int radius float

dx dy area translate int int return π·radius² x += dx y += dy

Figure 2: A simple Circle class.

12 graphics will be used to indicate assignments, function calls, recursive functions, matrices, return statements and others. HelloApp Control structures are a special case of statements. Envision currently has custom visualizations for if-else statements and loops.M Figureain 3 shows a factorial method that includes control statements. Circle An ’if’ control statement is indicated by a yellow diamond icon. Next to it is the condition. Below

n factorial int

n < 1 return 1 int result = 1 int i = 1; i < n; i++ result *= i

return result

Figure 3: Control statements in a factorial function. this horizontally arranged are the ’then’ and ’else’ branches indicated by light green and red background colors respectively. A ’loop’ is indicated by an icon with rotating arrows and a special border. Next to the icon is the header defining the loop including terminating or continuing conditions. As we’ve seen above visual abstractions exist for higher-level language features while specific details and execution statements are presented textually. However we find that adding additional visual effects or hints can be helpful even for text-based information. The reader might also notice that there are hardly any keywords visible. This is because the concepts that keywords embody are often better represented visually. For instance we can use color and/or position to indicate semantics. The structure of the ’if-else’ statement and the fields of a class are good examples for this.

3.3 User interaction

In order to improve programming productivity and appeal more to expert programmers input in Envision is mostly from the keyboard. In the current implementation the mouse is only used to zoom in and out using the wheel and to move visual objects. The programmer can simply click anywhere on an object and drag it to a different position. One exception is clicking on a piece of text which displays the text editing cursor instead. Mimicking text-based IDEs all text visible on the screen is editable directly. The two major features that define the feel of the user input system are discussed next.

3.3.1 Construction of visuals

Visual objects in Envision are not drawn or chosen from a palette. Instead they are constructed by typing. For example to create a new class the programmer would type ’class MyNewClass’ and press Enter. This will create a new class within the currently selected module. The class name will be set to ’MyNewClass’. The class will be selected and ready to receive input. The programmer can then proceed to add methods or fields in a similar fashion using the keyboard. Whenever a new entity or statement is added its parent automatically expands to fully include it. More complicated structures are also created from the keyboard. Typing ’if condition’ will create

13 a new if statement with the specified condition. Figure 4 shows this process. Similarly typing ’for header’ will create a new for loop with the specified header. This is illustrated in figure 5. Moreover it is possible to create statements basedh one the current context. For instance if the currently visible scope contains an integer variable named ’count’ and the user types ’for count’ the system wouldhe automatically generatehe a for loop that countstest from 0 to count - 1. As a counting variable the first non-used of i, j, k, l, m, ... will be used. This is demonstratedasdf in figure 6. test test asdf asdf val abs int val val abs abs int int val < 0

if val < 0

(a) Waiting for input (b) Typing a cre- (c) An if statement is ation command created a

a Figure 4: Creatinga an if statement. a a a top countdown int top countdown int int i = 0; i

for int i = 0; i

(a) For loop command (b) Resulting statement

Figure 5: Creating a for loop by specifying the complete header.

The concept behind this mechanism is the separation between what the user types and what the application fragments look like. In traditional IDEs and programming languages the programmer directly writes the final structure of the software. This has the drawback that a specific grammar must be learned and used. Such a grammar typically contains many syntactical rules, symbols, delimiters, etc. This is not very convenient but is necessary in order for the parser to be able to understand the language and in order for thea language to stay compact. In Envision syntax is kept to a minimum. The programmer expresses her desires by commands which the IDE interprets. It is responsiblea for figuring out what exactly thea user means and for creating the corresponding a a a elements size sumall int[ ] int elements size sumall int sum = 0 int[ ] int int i = 0; i

(a) For loop command (b) Resulting statement

Figure 6: Creating a for loop by specifying just the counting variable.

14 software fragment. This reduces the complexity of what needs to be typed especially with respect to syntax. At the same time concreteness is maintained - the visual result has a precise meaning.

3.3.2 Giving commands

By right-clicking anywhere on the screen the user shows a command prompt. It is used to quickly perform a variety of tasks which are dependent on the context:

• Create new software entities such as modules, classes, methods, fields, etc.

• Invoke IDE functions such as searching, opening views or projects and others. • Execute system commands like copying files or playing the next song in your audio player. • Perform user-defined actions.

The context of a prompt depends on where the programmer has clicked. After the user types a command it will be passed to the entity defining the context. This could be for example a method or a class. If the current context can not understand the command it is propagated to its parent. This process continues until some entity knows how to handle the command. If there is no such entity an error message appears directly under the prompt. Figure 7 shows how to create a new class. The use has right-clicked on the translate method. A method does not understand the ’class’ command and will pass it to its parent - a class. A class also does not understand this command and will propagate this to its enclosing module. The module interprets the class command by creating a new class with the specified name. If the context was the Application object itself, the command would fail since an Application does not know the ’class’ command. This is depicted in figure 8. Providing a wrong command would also result in a similar error message. This prompt provides great flexibility at the programmer’s fingertips. Common IDE functions can be invoked directly without opening menus or dialogs. Entire software fragments can easily be built by typing a single line.

3.4 Use case examples

3.4.1 Programming a Hello World application

Here we will see how to create a Hello World application step-by-step. After starting Envision an empty canvas appears with a prompt in the middle. Type the following commands:

1. app HelloWorld - creates a new application called HelloWorld. 2. module Main - creates the Main module within the application. 3. class Hello - creates a new class within the module.

4. method main - creates the main method of the class. 5. print "Hello World!" - the only statement of our program.

Notice how this does not require typing any special punctuation symbols or caring about format and syntax. This process is illustrated in figure 9.

15 HelloApp

Main Circle

filled boolean x int y int radius float

dx dy area translate int int return π·radius² x += dx class Square y += dy

(a) A prompt

HelloApp

Main Circle Square

filled boolean x int y int radius float

dx dy area translate int int return π·radius² x += dx y += dy

(b) The new class

Figure 7: Creating a new class by using a prompt. The context here is the translate method.

16 HelloApp

Main Circle Square

filled boolean x int y int radius float

dx dy area translate int int return π·radius² x += dx y += dy

class Triangle I have no clue what you mean by: class Triangle

Figure 8: An error displayed at the prompt. The context here is HelloApp which does not understand the ’class’ command.

HelloWorld

HelloWorld Main class Hello module Main

app HelloWorld

(a) (b) (c)

HelloWorld

Main HelloWorld Hello

Main Hello main method main print "Hello World"

(d) (e)

Figure 9: Creating a Hello World application.

17 3.4.2 Visualizing the source code of Envision itself

Envision is currently developed in C++. To get a better idea of how our approach scales to bigger applications we included a rudimentary C++ parser that was able to import Envision’s source code. Figure 10 shows the result.

Figure 10: Envision visualizing itself. The canvas is zoomed out so that the entire Application is visible. A raster image was used here, since the inclusion of the vector version would increase this document’s size by 100MB.

In the figure we can see in darker colors the different modules comprising the system. They are grouped with respect to color and position based on function. Blue modules pertain to visual- ization, gray to the program model, green to input handling, orange to C++ parsing, etc. Here we can see how Envision adds a new dimension to the Application. It allows the programmer to glance over the entire software piece and explore visually the relation between the different high-level modules. Zooming in on the ’constructs’ module we can see the different classes that belong to it in Figure 11. Classes here are also arranged in a special way: from left to right and from top to bottom they start with the top-most construct types such as Application and Module and go to more detailed types such as Statement or Loop. Similarly the programmer can arrange the methods within a class. In all classes in the constructs module the following convention applies: constructors and destructors appear immediately below the field declarations; core logic methods appear under the

18 Envision base constructs helpers parsers Construct CApplication CStatement CSequence Name Parser new_id static unsigned int modules CSet text CText vec typedef typename std::vector items std::vector name CText self Construct* app CApplication* module_name module_name class_name module_name class_name method_name createModule createClass createMethod id int iterator typedef typename vec::iterator QString QString QString QString QString QString parent Construct* parent getModules parent getText self ~Name getName CApplication CStatement Name CModule* m = app->getModules().insertNew(); CClass* c = app->getModule(module_name)->getClasses().insertNew(); CMethod* m = app->getModule(module_name)->getClass(class_name)->getMethods().insertNew(); reps std::list Construct* Construct* parent ~CSequence getItems Construct* return modules; return text; CSequence return name; m->getName().setText(module_name); c->getName().setText(class_name); m->getName().setText(method_name); revision int Construct* Parser ~Parser return items; erased static std::list iterator it = items.begin(); it != items.end(); ++it module_name == "Envision" module_name == "Envision" && class_name == "EnView" module_name == "Envision" && class_name == "EnView" && method_name == "EnView" name getModule *it m->setPos( 17000 , 20500 ); c->setPos( 1300 , 0 ); m->setPos( 12 , 3 ); QString i getId operator[] delete (*it); int module_name == "base" module_name == "Envision" && class_name == "Envision" module_name == "Envision" && class_name == "EnView" && method_name == "wheelEvent" return id; size_t i = 0; i< modules.size(); ++i parent ~Construct m->setPos( 0 , 0 ); c->setPos( 0 , 0 ); m->setPos( 0 , 450 ); Construct CLoop return items[i]; Color Construct* modules[i]->getName().getText() == name clearRepresentations(); getParent module_name == "constructs" module_name == "Envision" && class_name == "Ui_EnvisionClass" module_name == "Envision" && class_name == "EnView" && method_name == "scaleView" return modules[i]; body CSequence color QColor self Construct* return parent; header CText size m->setPos( 11500 , 0 ); c->setPos( 0 , 400 ); m->setPos( 0 , 750 ); return NULL; self ~Color getColor col return items.size(); Color setColor module_name == "helpers" module_name == "Envision" && class_name == "EnvisionClass" module_name == "Envision" && class_name == "EnView" && method_name == "customEvent" parent getHeader NULL Construct* QColor getRevision CLoop insertNewAfter return color; Construct* T* after = m->setPos( 16000 , 0 ); c->setPos( 1300 , 1000 ); m->setPos( 800 , 0 ); return header; color = col; return revision; C* new_item = new C(this); self->contentChanged(); path module_name == "handlers" module_name == "base" && class_name == "Construct" module_name == "Envision" && class_name == "Envision" && method_name == "~Envision" CModule getApp getBody after == NULL QString m->setPos( 12500 , 7500 ); c->setPos( 0 , 0 ); m->setPos( 400 , 0 ); rep rep getRepresentations addRepresentation removeRepresentation classes CSet Representation* Representation* return body; items.push_back(new_item); iterator it = items.begin(); it != items.end(); ++it app = new CApplication(NULL); module_name == "parsers" module_name == "base" && class_name == "Cursor" module_name == "Envision" && class_name == "Ui_EnvisionClass" && method_name == "setupUi" parent getClasses QDir dir(path); rep reps.remove(rep); CModule *it == after Visibility m->setPos( 24000 , 0 ); c->setPos( 1600 , 1800 ); m->setPos( 0 , 0 ); Construct* dir.dirName() != "." return classes; reps.push_back(rep); items.insert(it + 1, new_item); visibility Scope module_name == "styles" module_name == "base" && class_name == "Representation" module_name == "Envision" && class_name == "Ui_EnvisionClass" && method_name == "retranslateUi" setColor(QColor(0x40, 0xA0, 0x40)); app->getName().setText(dir.dirName()); break; self Construct* notifyRepresentations m->setPos( 0 , 18000 ); c->setPos( 0 , 1800 ); m->setPos( 0 , 650 ); name qDebug() << "Parsing application " << dir.dirName(); getClass CIfElse QString self getVisibility visibility parseDir(path); module_name == "vis" module_name == "constructs" && class_name == "CApplication" module_name == "base" && class_name == "Construct" && method_name == "notifyRepresentations" list::iterator it = reps.begin(); it != reps.end(); it++ header CText contentChanged(); Visibility setVisibility Construct* Scope return app; (*it)->constructChanged(); size_t i = 0; i < classes.size(); ++i then_branch CSequence return new_item; m->setPos( 0 , 5000 ); c->setPos( 0 , 0 ); m->setPos( 0 , 450 ); _branch _branch this->visibility = visibility; classes[i]->getName().getText() == name module_name == "vis_helpers" module_name == "constructs" && class_name == "CClass" module_name == "base" && class_name == "Construct" && method_name == "handleChildChange" NULL self->contentChanged(); clearRepresentations insertNewBefore path m->setPos( 6000 , 18000 ); c->setPos( 0 , 1000 ); m->setPos( 0 , 900 ); return classes[i]; parent getHeader T* before = parseDir CIfElse QString list::iterator it = reps.begin(); it != reps.end(); it++ Construct* module_name == "Envision" module_name == "constructs" && class_name == "CIfElse" module_name == "base" && class_name == "Construct" && method_name == "Construct" return NULL; return header; T* new_item = new T(this); Position QDir dir(path); (*it)->constructRemoved(); before == NULL m->setColor(QColor(0xA0, 0x40, 0xA0)); c->setPos( 1300 , 700 ); m->setPos( 0 , 50 ); allpos static std::vector y int x, self Construct* dir.dirName() == "debug" || dir.dirName() == "release" getThen items.insert(items.begin(), new_item); reps.clear(); iterator it = items.begin(); it != items.end(); ++it auto_placement bool module_name == "base" module_name == "constructs" && class_name == "CLoop" module_name == "base" && class_name == "Construct" && method_name == "~Construct" return; return then_branch; m->setColor(QColor(0xFF, 0xFF, 0x00)); c->setPos( 1300 , 300 ); m->setPos( 350 , 50 ); CClass *it == before self ~Position getX x y dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot); methods CSet Position setPos ; getElse items.insert(it, new_item); Construct* int int createModule(dir.dirName()); module_name == "constructs" && class_name == "CMethod" module_name == "base" && class_name == "Construct" && method_name == "contentChanged" return x; fields CSet break; QStringList filters; module_name == "constructs" c->setPos( 0 , 1900 ); m->setPos( 950 , 1050 ); return allpos.push_back(this); x = x_; filters << "*.h"; m->setColor(QColor(0x80, 0x80, 0x80)); parent getMethods else getY y = y_; module_name == "constructs" && class_name == "CModule" module_name == "base" && class_name == "Construct" && method_name == "addRepresentation" child CClass contentChanged(); dir.setNameFilters(filters); handleChildChange Construct* _branch; auto_placement = false; module_name == "helpers" Construct* return methods; return new_item; return y; QFileInfoList list = dir.entryInfoList(); c->setPos( 0 , 500 ); m->setPos( 0 , 300 ); self->contentChanged(); child m->setColor(QColor(0x80, 0x80, 0x80)); onChildChange int i = 0; i < list.size(); ++i module_name == "constructs" && class_name == "CPrompt" module_name == "base" && class_name == "Construct" && method_name == "removeRepresentation" int current_revision = revision; Construct* getFields elem isAutoPlaced auto_placement onChildChange(child); remove setAutoPlacement QFileInfo file = list.at(i); module_name == "handlers" c->setPos( 3100 , 2200 ); m->setPos( 450 , 300 ); T* bool revision == current_revision return fields; return auto_placement; parseFile(file.absoluteFilePath(), dir.dirName()); m->setColor(QColor(0x40, 0xA0, 0x40)); module_name == "constructs" && class_name == "CSequence" module_name == "base" && class_name == "Construct" && method_name == "getRepresentations" contentChanged items.erase( find(items.begin(), items.end(), elem)); bool old = auto_placement; child->notifyRepresentations(); CVariableDeclaration filters.clear(); module_name == "parsers" ++revision; name elem->erase(); auto_placement = auto_; c->setPos( 2400 , 0 ); m->setPos( 1150 , 300 ); erase getMethod filters << "*.cpp"; type CText contentChanged(); m->setColor(QColor(0xFF, 0x66, 0x00)); parent QString old != auto_ dir.setNameFilters(filters); module_name == "constructs" && class_name == "CSet" module_name == "base" && class_name == "Construct" && method_name == "clearRepresentations" erased.push_back(this); self->contentChanged(); list = dir.entryInfoList(); module_name == "styles" parent->handleChildChange(this); notifyRepresentations(); size_t i = 0; i< methods.size(); ++i parent getType c->setPos( 2400 , 1500 ); m->setPos( 0 , 600 ); CVariableDeclaration Construct* int i = 0; i < list.size(); ++i m->setColor(QColor(0x40, 0x40, 0xA0)); deleteErased methods[i]->getName().getText() == name return type; module_name == "constructs" && class_name == "CStatement" module_name == "base" && class_name == "Construct" && method_name == "erase" QFileInfo file = list.at(i); module_name == "vis" return methods[i]; c->setPos( 1300 , 0 ); m->setPos( 0 , 1100 ); list::iterator it = erased.begin(); it != erased.end(); ++it parseFile(file.absoluteFilePath(), dir.dirName()); CSet m->setColor(QColor(0x40, 0x40, 0xA0)); module_name == "constructs" && class_name == "CText" module_name == "base" && class_name == "Construct" && method_name == "deleteErased" (*it)->clearRepresentations(); return NULL; dir.setFilter(QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); vec typedef typename std::vector items std::vector module_name == "vis_helpers" delete (*it); list = dir.entryInfoList(); c->setPos( 1300 , 1600 ); m->setPos( 0 , 1200 ); iterator typedef typename vec::iterator m->setColor(QColor(0x40, 0x40, 0xA0)); erased.clear(); CText int i = 0; i < list.size(); ++i module_name == "constructs" && class_name == "CVariableDeclaration" module_name == "base" && class_name == "Construct" && method_name == "getRevision" name parent ~CSet getItems printAll printPosition return m; getField CSet QFileInfo file = list.at(i); c->setPos( 1300 , 1300 ); m->setPos( 1150 , 200 ); QString text QString Construct* return items; CModule* c = dynamic_cast (self); parseDir(file.absoluteFilePath()); iterator it = items.begin(); it != items.end(); ++it size_t i = 0; i < allpos.size(); ++i module_name == "helpers" && class_name == "Color" module_name == "base" && class_name == "Construct" && method_name == "getParent" size_t i = 0; i< fields.size(); ++i parent getText text c CText setText *it dynamic_cast (allpos[i]) c->setPos( 0 , 300 ); m->setPos( 1150 , 100 ); Construct* QString i qDebug() << "if (module_name == " << c->getName().getText() << " ) m->setPos(" << x << ", " << y << " );"; fields[i]->getName().getText() == name return text; operator[] delete (*it); int allpos[i]->printPosition(); module_name == "helpers" && class_name == "Name" module_name == "base" && class_name == "Construct" && method_name == "getId" this->text = text; return fields[i]; CClass* cc = dynamic_cast (self); contentChanged(); return items[i]; path module c->setPos( 0 , 0 ); m->setPos( 1150 , 0 ); Representation Cursor size_t i = 0; i < allpos.size(); ++i cc parseFile return NULL; QString QString CModule* mod = dynamic_cast (cc->getParent()->getParent()); module_name == "helpers" && class_name == "Position" module_name == "base" && class_name == "Construct" && method_name == "onChildChange" cons Construct* pos int dynamic_cast (allpos[i]) size mod && !cc->isAutoPlaced() qDebug() << "IN parseFile " << path; c->setPos( 0 , 900 ); m->setPos( 950 , 950 ); allpos[i]->printPosition(); cons ~Representation getConstruct constructRemoved pos_ CMethod QFile f(path); Representation Cursor return items.size(); qDebug() << "if (module_name == " << mod->getName().getText() module_name == "helpers" && class_name == "Visibility" module_name == "base" && class_name == "Cursor" && method_name == "Cursor" Construct* int insertNew !f.open(QIODevice::ReadOnly | QIODevice::Text) return cons; cons = NULL; << " && class_name == " << cc->getName().getText() << ") c->setPos(" << x << ", " << y << " );"; cons statements CSequence size_t i = 0; i < allpos.size(); ++i c->setPos( 0 , 600 ); m->setPos( 0 , 0 ); T* new_item = new T(this); return; cons cons->removeRepresentation(this); arguments CSequence items.push_back(new_item); dynamic_cast (allpos[i]) CMethod* ccc = dynamic_cast (self); module_name == "handlers" && class_name == "HApplication" module_name == "base" && class_name == "Representation" && method_name == "Representation" cons->addRepresentation(this); QTextStream text(&f); parent contentChanged(); allpos[i]->printPosition(); ccc QStringList lines = removeComments(text); c->setPos( 3600 , 900 ); m->setPos( 0 , 0 ); CMethod Construct* return new_item; CClass* cl = dynamic_cast (ccc->getParent()->getParent()); qDebug() << "======"; module_name == "handlers" && class_name == "HBase" module_name == "base" && class_name == "Representation" && method_name == "~Representation" getArguments CModule* mod = dynamic_cast (cl->getParent()->getParent()); int i = 0; i < lines.size(); ++i return arguments; cl && mod && !ccc->isAutoPlaced() c->setPos( 0 , 0 ); m->setPos( 400 , 0 ); qDebug() << "* " << lines[i]; constructChanged qDebug() << "if (module_name == " << mod->getName().getText() module_name == "handlers" && class_name == "HClass" module_name == "base" && class_name == "Representation" && method_name == "constructChanged" lines[i].size() > 300 getStatements << " && class_name == " << cl->getName().getText() << " && method_name == " << ccc->getName().getText() c->setPos( 3600 , 3900 ); m->setPos( 950 , 250 ); CPrompt qDebug() << " Line too long"; return statements; << ") m->setPos(" << x << ", " << y << " );"; abort(); module_name == "handlers" && class_name == "HClassField" module_name == "base" && class_name == "Representation" && method_name == "getConstruct" text CText c->setPos( 7300 , 0 ); m->setPos( 950 , 0 ); error1 CText lines = putOnNewLine(lines, "{"); error2 CText lines = putOnNewLine(lines, "}"); module_name == "handlers" && class_name == "HGlobal" module_name == "constructs" && class_name == "CApplication" && method_name == "CApplication" lines = putOnNewLine(lines, "if"); c->setPos( 3600 , 0 ); m->setPos( 0 , 0 ); parent getText getError1 getError2 CPrompt lines = putOnNewLine(lines, "else"); Construct* return text; return error1; return error2; lines = putOnNewLine(lines, "for"); module_name == "handlers" && class_name == "HIfElse" module_name == "constructs" && class_name == "CApplication" && method_name == "getModules" clearErrors lines = putOnNewLine(lines, "while"); c->setPos( 7300 , 3500 ); m->setPos( 450 , 0 ); error1.setText(""); lines = normalizeParenthesis(lines); error2.setText(""); module_name == "handlers" && class_name == "HMethodArgument" module_name == "constructs" && class_name == "CApplication" && method_name == "getModule" int i = 0; i < lines.size(); ++i contentChanged(); c->setPos( 7300 , 800 ); m->setPos( 450 , 100 ); lines[i] = lines[i].simplified(); module_name == "handlers" && class_name == "HModule" module_name == "constructs" && class_name == "CClass" && method_name == "CClass" int i = lines.size() - 1; i >= 0; --i c->setPos( 3600 , 3200 ); m->setPos( 0 , 0 ); lines[i].isEmpty() module_name == "handlers" && class_name == "HPrompt" module_name == "constructs" && class_name == "CClass" && method_name == "getMethods" lines.removeAt(i); c->setPos( 0 , 4400 ); m->setPos( 450 , 0 );

qDebug() << "======"; module_name == "handlers" && class_name == "HSequence" module_name == "constructs" && class_name == "CClass" && method_name == "getMethod" int i = 0; i < lines.size(); ++i c->setPos( 7300 , 1700 ); m->setPos( 450 , 200 ); qDebug() << "* " << lines[i]; module_name == "handlers" && class_name == "HStatement" module_name == "constructs" && class_name == "CClass" && method_name == "getFields" lines[i].size() > 300 c->setPos( 7300 , 2700 ); m->setPos( 450 , 100 ); qDebug() << " Line too long"; abort(); module_name == "handlers" && class_name == "HText" module_name == "constructs" && class_name == "CClass" && method_name == "getField" c->setPos( 0 , 2600 ); m->setPos( 450 , 500 ); parseFileEntry(module, lines); qDebug() << "OUT parseFile " << path; module_name == "handlers" && class_name == "UpdateEvent" module_name == "constructs" && class_name == "CIfElse" && method_name == "getHeader" Figure 11: The constructs module of Envision. Classes here implement the logical model of c->setPos( 8600 , 5000 ); m->setPos( 450 , 0 ); module_name == "parsers" && class_name == "Parser" module_name == "constructs" && class_name == "CIfElse" && method_name == "getThen" text lines lines separator removeComments normalizeParenthesis putOnNewLine c->setPos( 0 , 0 ); m->setPos( 450 , 100 ); QTextStream& QStringList& QStringList& QString module_name == "styles" && class_name == "SApplication" module_name == "constructs" && class_name == "CIfElse" && method_name == "getElse" QStringList res; QStringList res; QStringList res; programming entities such as classes, methods, statements etc. Zoom-in to see more detail. QString all = text.readAll(); res.append(""); res.append(""); c->setPos( 600 , 0 ); m->setPos( 450 , 200 ); bool in_comment = false; int level = 0; int i = 0; i < lines.length(); ++i module_name == "styles" && class_name == "SBox" module_name == "constructs" && class_name == "CLoop" && method_name == "getBody" bool in_string = false; bool in_string = false; qDebug() << " separating line : " << lines[i]; c->setPos( 0 , 0 ); m->setPos( 450 , 100 ); QChar match; QChar match; QStringList parts = lines[i].simplified().split(separator); res.append(""); QChar val; module_name == "styles" && class_name == "SClass" module_name == "constructs" && class_name == "CLoop" && method_name == "getHeader" !res.last().isEmpty() int i = 0; i < all.size(); ++i QString line; c->setPos( 1900 , 0 ); m->setPos( 450 , 0 ); int i = 0; i < lines.length(); ++i res.append(""); in_comment module_name == "styles" && class_name == "SClassName" module_name == "constructs" && class_name == "CMethod" && method_name == "CMethod" line = lines[i]; bool in_string = false; all[i] == match in_string c->setPos( 900 , 2900 ); m->setPos( 0 , 0 ); res.last().append(line); QChar match; res.last().append(all[i]); QChar val; match == '\n' all[i] == '\n' int k = 0; k < line.size(); ++k module_name == "styles" && class_name == "SIfElse" module_name == "constructs" && class_name == "CMethod" && method_name == "getStatements" in_comment = false; all[i] == match && all[i - 1] != SLASH res.append(""); int k = 0; k < parts.size(); ++k match == '*' && all[i + 1] == '/' all[i] == '/' && all[i + 1] == '/' val = line[k]; c->setPos( 4200 , 0 ); m->setPos( 450 , 150 ); res.last().size() > 0 in_string = false; match = '/'; match == '/' in_comment = true; all[i] == '/' && all[i + 1] == '*' in_string && val == match && line[k - 1] != SLASH int c = 0; c < parts[k].size(); ++c module_name == "styles" && class_name == "SInvisible" module_name == "constructs" && class_name == "CMethod" && method_name == "getArguments" res.append(""); match = '\n'; constructors; getters and setters appear to the right; auxiliary methods, if any, appear in the lower in_comment = false; in_comment = true; all[i] == '\'' in_string = false; !in_string && (val == '\'' || val == '"') val = parts[k][c]; c->setPos( 5200 , 2300 ); m->setPos( 450 , 50 ); match = '*'; res.last().append(val); in_string = true; all[i] == '"' in_string = true; !in_string && val == '(' module_name == "styles" && class_name == "SLoop" module_name == "constructs" && class_name == "CModule" && method_name == "CModule" match = '\''; match = val; in_string && val == match && (c == 0 || parts[k][c - 1] != SLASH) in_string = true; res.last().append(all[i]); ++level; !in_string && val == ')' c->setPos( 5000 , 0 ); m->setPos( 0 , 0 ); res.last().append(all[i]); in_string = false; match = '"'; !in_string && (val == '\'' || val == '"') --level; module_name == "styles" && class_name == "SMethod" module_name == "constructs" && class_name == "CModule" && method_name == "getClasses" res.last().append(all[i]); in_string = true; right corner. match = val; c->setPos( 2600 , 0 ); m->setPos( 450 , 0 ); level == 0 && !res.last().isEmpty() module_name == "styles" && class_name == "SMethodName" module_name == "constructs" && class_name == "CModule" && method_name == "getClass" k + 1 < parts.size() res.append(""); c->setPos( 1300 , 2900 ); m->setPos( 450 , 100 ); return res; bool allow_new_line = !in_string; module_name == "styles" && class_name == "SModule" module_name == "constructs" && class_name == "CPrompt" && method_name == "CPrompt" return res; separator[0].isLetter() c->setPos( 1200 , 0 ); m->setPos( 0 , 0 ); !parts[k].isEmpty() && val.isLetterOrNumber() module lines module_name == "styles" && class_name == "SModuleName" module_name == "constructs" && class_name == "CPrompt" && method_name == "getText" We can see how one can get an overview of an entire module or even the entire application by parseFileEntry allow_new_line = false; QString QStringList& c->setPos( 500 , 2900 ); m->setPos( 500 , 0 ); !parts[k + 1].isEmpty() && parts[k + 1][0].isLetterOrNumber() qDebug() << "IN parseFileEntry ------" << lines.first(); module_name == "styles" && class_name == "SPrompt" module_name == "constructs" && class_name == "CSequence" && method_name == "CSequence" allow_new_line = false; lines.isEmpty() == false c->setPos( 4400 , 2300 ); m->setPos( 0 , 0 ); allow_new_line && !res.last().isEmpty() zooming out. Navigating around the application space is currently achieved by zooming in and qDebug() << "file entry next line " << lines.first(); module_name == "styles" && class_name == "SStackTrace" module_name == "constructs" && class_name == "CSequence" && method_name == "~CSequence" res.append(""); (lines.first().startsWith("class") || lines.first().contains("::")) == false|| lines.first().split("::").size() > 2 c->setPos( 4400 , 2900 ); m->setPos( 350 , 0 ); lines.removeFirst(); res.last().append(separator); module_name == "styles" && class_name == "SText" module_name == "constructs" && class_name == "CSequence" && method_name == "getItems" lines.isEmpty() allow_new_line c->setPos( 0 , 2900 ); m->setPos( 1150 , 0 ); break; res.append(""); out, using the scroll bars or using the arrow keys on the keyboard. module_name == "styles" && class_name == "SType" module_name == "constructs" && class_name == "CSequence" && method_name == "size" lines.isEmpty() c->setPos( 1700 , 2900 ); m->setPos( 1150 , 250 ); res.last().isEmpty() break; res.removeLast(); module_name == "styles" && class_name == "SVarInfo" module_name == "constructs" && class_name == "CSequence" && method_name == "operator[]" qDebug() << "file entry next interesting line: " << lines.first(); c->setPos( 2100 , 2900 ); m->setPos( 1150 , 100 ); qDebug() << "Finished pre-processing separators"; lines.first().startsWith("class") && !lines.first().contains(';') return res; module_name == "vis" && class_name == "ClassIcon" module_name == "constructs" && class_name == "CSequence" && method_name == "insertNewAfter" QString class_name = lines.first().split(' ')[1].split(':').first(); lines.first().contains("::") && lines.first().contains('(') && !lines.first().contains('=') createClass(module, class_name); c->setPos( 6500 , 1300 ); m->setPos( 0 , 300 ); In our experience of visualizing Envision’s source code, one of the most useful features was to see qDebug() << " recognized a method definition "; lines.removeFirst(); lines.first() != "{" QStringList s = lines.first().split("::"); module_name == "vis" && class_name == "IfElseIcon" module_name == "constructs" && class_name == "CSequence" && method_name == "insertNewBefore" lines.removeFirst(); s[0] = s[0].simplified(); c->setPos( 7700 , 7000 ); m->setPos( 0 , 700 ); s[1] = s[1].simplified(); parseClassDeclaration(module, class_name, lines); module_name == "vis" && class_name == "LoopIcon" module_name == "constructs" && class_name == "CSequence" && method_name == "remove" QString class_name = s[0].split(' ').last().simplified(); methods or classes side by side. QString method_name = s[1].split('(').first().simplified(); c->setPos( 7500 , 5500 ); m->setPos( 0 , 1100 ); CClass* cl = app->getModule(module)->getClass(class_name); module_name == "vis" && class_name == "MethodIcon" module_name == "constructs" && class_name == "CSet" && method_name == "CSet" cl c->setPos( 8000 , 4200 ); m->setPos( 0 , 0 ); CMethod* m = cl->getMethod(method_name); qDebug() << "No such class " << class_name; module_name == "vis" && class_name == "VApplication" module_name == "constructs" && class_name == "CSet" && method_name == "~CSet" lines.first() != "{" CMethod* m = (CMethod*) NULL; qDebug() << m->getName().getText(); c->setPos( 6000 , 0 ); m->setPos( 300 , 0 ); lines.removeFirst(); module_name == "vis" && class_name == "VBase" module_name == "constructs" && class_name == "CSet" && method_name == "getItems" parseBody(m->getStatements(), lines); c->setPos( 0 , 0 ); m->setPos( 1150 , 0 );

qDebug() << "OUT parseFileEntry "; module_name == "vis" && class_name == "VBox" module_name == "constructs" && class_name == "CSet" && method_name == "size" c->setPos( 0 , 3400 ); m->setPos( 1150 , 250 );

module_name == "vis" && class_name == "VBoxed" module_name == "constructs" && class_name == "CSet" && method_name == "operator[]" module class_name lines c->setPos( 700 , 10900 ); m->setPos( 1150 , 100 ); parseClassDeclaration vis 3.5 Case study: call stack visualization QString QString QStringList& VBase VApplication module_name == "vis" && class_name == "VClass" module_name == "constructs" && class_name == "CSet" && method_name == "insertNew" qDebug() << "IN parseClassDeclaration " << lines.first(); c->setPos( 6000 , 1300 ); m->setPos( 0 , 300 ); bounding_rect QRectF parent VBase* app CApplication* lines.first() != "{" cursor static Cursor handler HBase* modules VSet qDebug() << "Parsing a class that does not begin with {"; module_name == "vis" && class_name == "VClassField" module_name == "constructs" && class_name == "CStatement" && method_name == "CStatement" revision int name VText c->setPos( 7800 , 2300 ); m->setPos( 0 , 0 ); is_updating_rep bool lines.removeFirst(); is_updating_cons bool parent app int level = 1; module_name == "vis" && class_name == "VDummy" module_name == "constructs" && class_name == "CStatement" && method_name == "getText" VApplication bool isprivate = true; default_handler static HBase* VBase* CApplication* c->setPos( 7800 , 9000 ); m->setPos( 400 , 0 ); vis_prompt static VPrompt* bool isprotected = false; setEventHandler( HApplication::getInstance()); cons_prompt static CPrompt* bool ispublic = false; module_name == "vis" && class_name == "VFields" module_name == "constructs" && class_name == "CText" && method_name == "CText" setStyle( SApplication::getNormalStyle() ); need_updating static std::list c->setPos( 6000 , 2300 ); m->setPos( 12 , 1 ); setClient( &modules ); This style of software representation could offer interesting features in terms or error reporting and level > 0 focused_vis static VBase* name.setPos(15,15); lines.first() == "{" module_name == "vis" && class_name == "VGrid" module_name == "constructs" && class_name == "CText" && method_name == "getText" focused_cons static Construct* cons_with_prompt static Construct* level++; c->setPos( 0 , 6600 ); m->setPos( 350 , 0 ); lines.removeFirst(); module_name == "vis" && class_name == "VIfElse" module_name == "constructs" && class_name == "CText" && method_name == "setText" parent cons ~VBase getCursor VModule continue; VBase VBase* Construct* debugging. We explored a sample call stack visualization presented in figure 12. The methods from c->setPos( 6000 , 7000 ); m->setPos( 550 , 0 ); vis_prompt && vis_prompt->getParent() == this return cursor; mod CModule* lines.first() == "}" classes VSet module_name == "vis" && class_name == "VLinear" module_name == "constructs" && class_name == "CVariableDeclaration" && method_name == "getType" cons != NULL && cons == cons_with_prompt removePrompt(); level--; name VText showPrompt(); getParent lines.removeFirst(); c->setPos( 3300 , 8600 ); m->setPos( 450 , 0 ); scene() big_name static bool setConsWithPrompt( NULL); return parent; continue; module_name == "vis" && class_name == "VLoop" module_name == "helpers" && class_name == "Color" && method_name == "~Color" scene()->removeItem(this); style SModule the stack are arranged from left to right in the order in which they were called. The highlighted cons != NULL && cons == focused_cons lines.first().contains('(') c->setPos( 6000 , 5500 ); m->setPos( 300 , 0 ); need_updating.remove( this ); getHandler handler parent mod setFlag(ItemIsFocusable); setEventHandler VModule QString method_name = lines.first().left(lines.first().indexOf('(')).simplified().split(' ').last(); lines.first().simplified() == "public:" module_name == "vis" && class_name == "VMethod" module_name == "helpers" && class_name == "Color" && method_name == "getColor" HBase* VBase* CModule* setFocus(); return handler; qDebug() << "class creating method " << method_name << " " << lines.first(); ispublic = true; lines.first().simplified() == "private:" c->setPos( 6000 , 4200 ); m->setPos( 800 , 0 ); this->handler = handler; CMethod* m = createMethod(module, class_name, method_name); setEventHandler(HModule::getInstance()); isprivate = false; ispublic = false; lines.first().simplified() == "protected:" boundingRect style.background.setColor( mod_->getColor() ); isprivate isprotected = false; module_name == "vis" && class_name == "VMethodArgument" module_name == "helpers" && class_name == "Color" && method_name == "setColor" line(orange) in each method is where the call to the next one occurs. Vertically the methods are isprivate = true; setStyle(&style); m->setVisibility(Visibility::PRIVATE); lines.removeFirst(); ispublic = false; lines.first().endsWith(';') && !lines.first().contains('{') c->setPos( 6900 , 4200 ); m->setPos( 1050 , 0 ); return bounding_rect; isprotected = false; setClient(&classes); isprivate = false; lines.removeFirst(); QString decl = lines.first(); lines.removeFirst(); name.setPos(15, 15); isprotected isprotected = true; module_name == "vis" && class_name == "VModule" module_name == "helpers" && class_name == "Name" && method_name == "Name" type decl.chop(1); name.setZValue(10); m->setVisibility(Visibility::PROTECTED); lines.removeFirst(); c->setPos( 6000 , 400 ); m->setPos( 0 , 0 ); decl = decl.simplified(); return Type; name.setStyle(SModuleName::getNormalStyle()); aligned to this line to make it easier to trace. Information about the run-time argument values ispublic QString var = decl.split(' ').last(); module_name == "vis" && class_name == "VPrompt" module_name == "helpers" && class_name == "Name" && method_name == "~Name" QString type = decl.left(decl.length() - var.length() - 1); c->setPos( 7300 , 9000 ); m->setPos( 300 , 0 ); requestPrompt removePrompt switchBigNames m->setVisibility(Visibility::PUBLIC); var.startsWith('*') parseMethodArguments(module, class_name, method_name, lines); module_name == "vis" && class_name == "VRuntimeVariableInfo" module_name == "helpers" && class_name == "Name" && method_name == "getName" !hasPrompt() vis_prompt != NULL big_name = !big_name; var = var.right(var.length() - 1); lines.first().contains(';') == false && lines.first().contains("{") == false removePrompt(); vis_prompt->focus(); delete vis_prompt; type.append('*'); c->setPos( 7400 , 10800 ); m->setPos( 650 , 0 ); updateRepresentation cons_prompt = new CPrompt(NULL); vis_prompt = NULL; appears above each method. A useful feature here is the juxtaposition of all relevant methods on lines.removeFirst(); module_name == "vis" && class_name == "VSequence" module_name == "helpers" && class_name == "Position" && method_name == "Position" qDebug() << "var " << var << " type" << type; focus(&cons_prompt->getText()); delete cons_prompt; big_name lines.first().contains('{') CVariableDeclaration* v = app->getModule(module)->getClass(class_name)->getFields().insertNew(); c->setPos( 1400 , 8600 ); m->setPos( 0 , 0 ); vis_prompt = new VPrompt(this, cons_prompt); cons_prompt = NULL; name.setStyle(SModuleName::getHugeStyle()); name.setStyle(SModuleName::getNormalStyle()); v->getName().setText(var); cursor.pos = 0; parseBody(app->getModule(module)->getClass(class_name)->getMethod(method_name)->getStatements(), lines); lines.removeFirst(); module_name == "vis" && class_name == "VSet" module_name == "helpers" && class_name == "Position" && method_name == "~Position" name.setPos(bounding_rect.width() / 2 - name.boundingRect().width() / 2, bounding_rect.height() / 2- name.boundingRect().height() / 2); name.setPos(15, 15); v->getType().setText(type); return vis_prompt; qDebug() << "MID parseClassDeclaration " << lines.first() << " level " << level; isprivate c->setPos( 0 , 8600 ); m->setPos( 300 , 0 ); VBox::updateRepresentation(); one screen. This eliminates the need to switch between different files or classes as is the case in v->setVisibility(Visibility::PRIVATE); module_name == "vis" && class_name == "VSpacer" module_name == "helpers" && class_name == "Position" && method_name == "setPos" showPrompt x y showPrompt isprotected c->setPos( 0 , 10900 ); m->setPos( 1150 , 0 ); int int v->setVisibility(Visibility::PROTECTED); module_name == "vis" && class_name == "VStackTrace" module_name == "helpers" && class_name == "Position" && method_name == "setAutoPlacement" requestPrompt(); vis_prompt->setPos(x - vis_prompt->bounding_rect.width() / 2, y - vis_prompt->bounding_rect.height() / 2); VClass ClassIcon modern IDEs. ispublic c->setPos( 6000 , 10800 ); m->setPos( 1150 , 200 ); bounding_rect.width() > 0 cclass CClass* v->setVisibility(Visibility::PUBLIC); module_name == "vis" && class_name == "VStatement" module_name == "helpers" && class_name == "Position" && method_name == "getX" showPrompt(bounding_rect.width() / 2, bounding_rect.height() / 2); showPrompt(50, 50); icon ClassIcon parent lines.removeFirst(); c->setPos( 6000 , 5100 ); m->setPos( 800 , 0 ); ClassIcon hasPrompt constructChanged contents VLinear VBase* module_name == "vis" && class_name == "VText" module_name == "helpers" && class_name == "Position" && method_name == "getY" fields VFields return vis_prompt != NULL && vis_prompt->getParent() == this; need_updating.push_back(this); updateRepresentation(); methods VSet c->setPos( 6000 , 9000 ); m->setPos( 800 , 100 ); qDebug() << "OUT parseClassDeclaration "; name VBoxed module_name == "vis_helpers" && class_name == "VisFactory" module_name == "helpers" && class_name == "Position" && method_name == "isAutoPlaced" cons visualizationChanged setConsWithPrompt Construct* parent cclass c->setPos( 0 , 100 ); m->setPos( 800 , 200 ); updateRepresentationFrame(); VClass VBase* CClass* cons_with_prompt = cons; updateRepresentation return c; module_name == "helpers" && class_name == "Position" && method_name == "printPosition" focused_cons = NULL; child setEventHandler(HClass::getInstance()); s lines onChildChange bounding_rect = QRectF(0, 0, SIZE, SIZE); parseBody m->setPos( 400 , 600 ); focused_vis = NULL; VBase* name.item.setStyle(SClassName::getNormalStyle()); CSequence& QStringList& module_name == "helpers" && class_name == "Position" && method_name == "printAll" setStyle(SClass::getNormalStyle()); qDebug() << "IN parseBody " << lines.first(); !is_updating_rep && !is_updating_cons painter option widget name.setStyle(SClass::getTitleStyle()); paint m->setPos( 0 , 600 ); QPainter* const QStyleOptionGraphicsItem* QWidget* lines.first() != "{" updateRepresentationFrame(); contents.setOptions(true,false, 10); lines.removeFirst(); module_name == "helpers" && class_name == "Visibility" && method_name == "Visibility" contents.addItem(&fields); QRadialGradient gradient(SIZE / 2, SIZE / 2, SIZE/2, SIZE / 2, SIZE / 2); lines.first() == "for" || lines.first() == "while" int level = 1; contents.addItem(&methods); gradient.setColorAt(0, Qt::white); parseLoop(s, lines); lines.first() == "if" m->setPos( 0 , 0 ); setClient(&contents); gradient.setColorAt(1, Qt::transparent); 4 Discussion level > 0 event module_name == "helpers" && class_name == "Visibility" && method_name == "getVisibility" mouseMoveEvent parseIfElse(s, lines); !lines.first().contains(';') QGraphicsSceneMouseEvent* setHeader(&name); painter->setBrush(QBrush(gradient)); qDebug() << "IN parseBody block current line " << lines.first(); m->setPos( 800 , 0 ); updateRepresentation updateIfConstructChanged setIcon(&icon); QPen p(Qt::NoPen); qDebug() << "IN parseBody single add " << lines.first(); QGraphicsItem::mouseMoveEvent(event); lines.first() == "}" painter->setPen(p); s.insertNewAfter ()->getText().setText(lines.first()); module_name == "helpers" && class_name == "Visibility" && method_name == "setVisibility" getConstruct() == NULL || revision != getConstruct()->getRevision() handler->mouseMoveEvent(event, this); level--; painter->drawRect(0,0,SIZE,SIZE); lines.removeFirst(); is_updating_cons = true; m->setPos( 1050 , 0 ); painter->setBrush(Qt::white); lines.first() == "{" updateRepresentationFrame event qDebug() << "IN parseBody single add " << lines.first(); updateAllChildren(); mouseReleaseEvent QPen pen(Qt::black, 1); module_name == "handlers" && class_name == "HApplication" && method_name == "HApplication" QGraphicsSceneMouseEvent* s.insertNewAfter ()->getText().setText(lines.first()); level++; is_updating_rep == false Position* p = dynamic_cast (getConstruct()); painter->setPen(pen); lines.removeFirst(); m->setPos( 4 , 9 ); p && p->isAutoPlaced() == false handler->mouseReleaseEvent(event, this); int width = SIZE / 2 - 4; lines.first() == "for" || lines.first() == "while" is_updating_rep = true; QGraphicsItem::mouseReleaseEvent(event); int height = SIZE / 3; module_name == "handlers" && class_name == "HApplication" && method_name == "command" prepareGeometryChange(); setPos(p->getX(), p->getY()); parseLoop(s, lines); lines.first() == "if" painter->drawRect((SIZE - width) / 2, 2, width, height); updateRepresentation(); m->setPos( 1600 , 150 ); updateRepresentationFrame(); event painter->drawRect(1, SIZE - 2 - height, width, height); parseIfElse(s, lines); level > 0 is_updating_rep = false; keyPressEvent getConstruct() As part of this feasibility study we developed a mock-up version of Envision which was presented module_name == "handlers" && class_name == "HApplication" && method_name == "getInstance" QKeyEvent* painter->drawRect(SIZE - 1 - width, SIZE - 2 - height, width, height); qDebug() << "IN parseBody block add " << lines.first(); parent painter->drawLine(1 + width / 2, SIZE / 2, SIZE - 1 - width / 2, SIZE / 2); m->setPos( 700 , 0 ); revision = getConstruct()->getRevision(); handler->keyPressEvent(event, this); s.insertNewAfter ()->getText().setText(lines.first()); painter->drawLine(SIZE / 2, 2 + height, SIZE / 2, SIZE / 2); parent->onChildChange(this); is_updating_cons = false; lines.removeFirst(); module_name == "handlers" && class_name == "HApplication" && method_name == "keyDigit" painter->drawLine(1 + width / 2, SIZE / 2, 1 + width / 2, SIZE - 2 - height); painter->drawLine(SIZE - 1 - width / 2, SIZE / 2, SIZE - 1 - width / 2, SIZE - 2 - height); m->setPos( 0 , 100 ); in the previous section. It focuses only on the core concepts of visualization and user input. Here lines.first() != "}" module_name == "handlers" && class_name == "HBase" && method_name == "HBase" updateModifiedItems VFields VClassField qDebug() << "Illegal exit from body "; m->setPos( 21 , 1 ); fields CSet* dec CVariableDeclaration* CMethod* m = (CMethod*) NULL; std::list::iterator it = need_updating.begin(); it != need_updating.end(); ++it sections VLinear v VLinear qDebug() << m->getName().getText(); module_name == "handlers" && class_name == "HBase" && method_name == "tokenize" (*it)->updateIfConstructChanged(); sec_private VLinear name VText we discuss our experiences with the application and comment on how it compares to traditional lines.removeFirst(); m->setPos( 2450 , 2000 ); need_updating.clear(); sec_public VLinear type VText qDebug() << "OUT parseBody "; module_name == "handlers" && class_name == "HBase" && method_name == "keyLetter" sec_protected VLinear handlers vis_fields std::list parent dec getType m->setPos( 600 , 1800 ); VClassField updateAllChildren VBase* CVariableDeclaration* HBase HGlobal HClassField return type; module_name == "handlers" && class_name == "HBase" && method_name == "keyDigit" parent fields ~VFields VFields setEventHandler(HClassField::getInstance()); the_instance static HBase the_instance static HGlobal the_instance static HClassField module class_name method_name lines int i = 0; i < children().length(); ++i parseMethodArguments m->setPos( 600 , 1650 ); VBase* CSet* IDEs. current_view static QGraphicsView* clearAll(); name.setZValue(v.zValue() + 1); QString QString QString QStringList& VBase* child = dynamic_cast ((children())[i]); getName sections.setOptions(false, false, DISTANCE); type.setZValue(v.zValue() + 1); HGlobal getInstance HClassField getInstance module_name == "handlers" && class_name == "HBase" && method_name == "keySymbol" qDebug() << "IN parseMethodArguments " << lines.first(); child return name; HBase ~HBase getInstance view setClient(§ions); type.setStyle(SType::getNormalStyle()); setView return &the_instance; return &the_instance; m->setPos( 600 , 750 ); QGraphicsView* tokens vis QString line = lines.first(); child->updateIfConstructChanged(); updateRepresentation(); v.setOptions(false, false, 5); return &the_instance; command QStringList VBase* !line.contains(')') module_name == "handlers" && class_name == "HBase" && method_name == "keyArrow" v.addItem(&name); current_view = view; event vis bool processed = false; keyEnter v.addItem(&type); view QKeyEvent* VBase* lines.removeFirst(); m->setPos( 600 , 600 ); setStyle(SInvisible::getNormalStyle()); tokens.size() > 0 line += lines.first(); module_name == "handlers" && class_name == "HBase" && method_name == "keySpace" setClient(&v); return current_view; CVariableDeclaration* vd = dynamic_cast (vis->getConstruct()); It is important to note that the mock-up Envision application can not be used to develop soft- tokens[0] == "app" || tokens[0] == "application" QStringList pairs = line.split('(')[1].split(')')[0].simplified().split(','); CSet* set = dynamic_cast*> (vd->getParent()); m->setPos( 600 , 450 ); tokens vis CApplication* app = new CApplication(NULL); vis->scene()->clearFocus(); pairs.size() == 0 command module_name == "handlers" && class_name == "HBase" && method_name == "keyTab" focus vis cons HasFocusInHierarchy QStringList VBase* tokens.size() > 1 CVariableDeclaration* new_var = set->insertNew(); return; focus VBase* Construct* event vis new_var->setVisibility( vd->getVisibility() ); m->setPos( 600 , 900 ); clearAll mouseMoveEvent return false; app->getName().setText( tokens[1] ); bool has_focus = hasFocus(); QGraphicsSceneMouseEvent* VBase* VBase::focus(&new_var->getName()); pairs.size() == 1 && pairs[0].size() < 1 focused_cons = NULL; module_name == "handlers" && class_name == "HBase" && method_name == "keyShift" !has_focus sections.removeAllItems(); event vis event vis VApplication* vapp = new VApplication(NULL, app); vis->getCursor().pos = 0; return; keyPressEvent keyEnter mouseMove(event, vis); focused_vis = vis; sec_private.removeAllItems(); QKeyEvent* VBase* QKeyEvent* VBase* vis->scene()->addItem(vapp); m->setPos( 600 , 1050 ); int i = 0; i < children().length(); ++i event->isAccepted() str vis focused_vis = NULL; sec_public.removeAllItems(); vapp->setPos(-vapp->boundingRect().width()/2, -vapp->boundingRect().height()/2); int i = 0; i < pairs.size(); ++i switch (event->key()) event->ignore(); processCommand event vis module_name == "handlers" && class_name == "HBase" && method_name == "keyControl" focusgeedt_Pcroonms p=t Cconss; VBase* child = dynamic_cast ((children())[i]); sec_protected.removeAllItems(); QApplication::postEvent(view(), new UpdateEvent()); QString VBase* keyControl { CPrompt* p = dynamic_cast ( vis->getConstruct() ); QString var = pairs[i].split(' ').last(); QKeyEvent* VBase* m->setPos( 600 , 1200 ); return cons_prompt; child std::list::iterator it = vis_fields.begin(); it != vis_fields.end(); ++it case Qt::Key_A ... Qt::Key_B: QStringList tokens = tokenize(str); delete vis; QString type; VClassField* cf = dynamic_cast (vis); has_focus = child->HasFocusInHierarchy(); keyLetter(event, vis); event vis bool processed = false; delete p; var.length() == pairs[i].length() module_name == "handlers" && class_name == "HBase" && method_name == "keyAlt" delete (*it); keySpace cf->getCursor().pos = 0; break; QKeyEvent* VBase* VBase* elem = vis; vapp->showPrompt(); has_focus var = ""; type = pairs[i].left(pairs[i].length() - var.length() - 1); m->setPos( 600 , 1350 ); vis_fields.clear(); case Qt::Key_0 ... Qt::Key_9: event vis processed = true; ! cf->getName().HasFocusInHierarchy() keyLetter(event, vis); mouseReleaseEvent elem && !processed type = pairs[i]; break; keyDigit(event, vis); QGraphicsSceneMouseEvent* VBase* cf->getName().setFocus(); cf->getType().setFocus(); module_name == "handlers" && class_name == "HBase" && method_name == "keyDelBackspace" processed = elem->getHandler()->command(tokens, elem); tokens[0] == "save" updateRepresentation break; buttonRelease(event, vis); var.startsWith('*') m->setPos( 600 , 1500 ); elem = elem->getParent(); Position::printAll(); return has_focus; case Qt::Key_Underscore: event vis event->isAccepted() var = var.right(var.length() - 1); vis_fields.size() != fields->size() keyArrow 19 module_name == "handlers" && class_name == "HBase" && method_name == "keyEnter" keyLetter(event, vis); QKeyEvent* VBase* elem == NULL && !processed return processed; type.append('*'); clearAll(); QApplication::postEvent(view(), new UpdateEvent()); m->setPos( 600 , 300 ); break; HBase* global = HGlobal::getInstance(); event->ignore(); qDebug() << "var " << var << " type" << type; int i = 0; i < fields->size(); ++i case Qt::Key_Exclam ... Qt::Key_Slash: HMethodArgument processed = global->command(tokens, vis); CVariableDeclaration* v = module_name == "handlers" && class_name == "HBase" && method_name == "keyOther" keySymbol(event, vis); VClassField* v; the_instance static HMethodArgument app->getModule(module)->getClass(class_name)->getMethod(method_name)->getArguments().insertNewAfter< break; CPrompt* p = VBase::getPromptCons(); m->setPos( 600 , 1950 ); (*fields)[i]->getVisibility() == Visibility::PRIVATE event vis event vis processed && p CVariableDeclaration> (); case Qt::Key_Colon ... Qt::Key_At: keySymbol mouseMove module_name == "handlers" && class_name == "HBase" && method_name == "buttonRelease" VBox QKeyEvent* VBase* QGraphicsSceneMouseEvent* VBase* HApplication HMethodArgument getInstance v->getName().setText(var); v = new VClassField(&sec_private, (*fields)[i]); keySymbol(event, vis); p->clearErrors(); sec_private.addItem(v); return &the_instance; v->getType().setText(type); m->setPos( 1300 , 1050 ); client_width int break; keyLetter(event, vis); vis the_instance static HApplication !processed && p client_height int case Qt::Key_BracketLeft ... Qt::Key_AsciiCircum: qDebug() << "OUT parseMethodArguments " << lines.first(); module_name == "handlers" && class_name == "HBase" && method_name == "mouseMove" (*fields)[i]->getVisibility() == Visibility::PUBLIC Position* p = dynamic_cast (vis->getConstruct()); event->ignore(); p->getError1().setText("I have no clue what you mean by:"); header_width int keySymbol(event, vis); HApplication getInstance m->setPos( 1300 , 750 ); v = new VClassField(&sec_public, (*fields)[i]); p header_height int break; event vis p->getError2().setText( p->getText().getText() ); sec_public.addItem(v); keyTab return &the_instance; icon_width int case Qt::Key_QuoteLeft ... Qt::Key_AsciiTilde: QKeyEvent* VBase* p->setPos(vis->x(), vis->y()); p->contentChanged(); module_name == "handlers" && class_name == "HBase" && method_name == "processCommand" icon_height int (*fields)[i]->getVisibility() == Visibility::PROTECTED keySymbol(event, vis); m->setPos( 2500 , 350 ); event->ignore(); event->accept(); event vis event vis s lines line header rest outter_width int break; keyDigit keyEnter parseIfElse getLoopIfElseHeader v = new VClassField(&sec_protected, (*fields)[i]); QKeyEvent* VBase* QKeyEvent* VBase* CSequence& QStringList& QString QString& QString& module_name == "handlers" && class_name == "HBase" && method_name == "command" outter_height int case Qt::Key_Return: sec_protected.addItem(v); tokens vis keyEnter(event, vis); command CVariableDeclaration* vd = dynamic_cast (vis->getConstruct()); qDebug() << "IN parseIfElse " << lines.first(); m->setPos( 2500 , 200 ); snap_to_grid int event vis event vis event->key() == Qt::Key_0 QStringList VBase* !line.startsWith('(') client VBase* vis_fields.push_back(v); break; keyShift buttonRelease CSequence* seq = dynamic_cast*> (vd->getParent()); lines.removeFirst(); QKeyEvent* VBase* QGraphicsSceneMouseEvent* VBase* VModule::switchBigNames(); qDebug() << "Extracting incorrect header: " << line; module_name == "handlers" && class_name == "HBase" && method_name == "getInstance" header VBase* case Qt::Key_Enter: bool processed = false; vis->scene()->clearFocus(); QString header; sections.addItem(&sec_public); CMethod* m = (CMethod*) NULL; icon VBase* keyEnter(event, vis); event->ignore(); event->button() == Qt::RightButton int i = 0; i < vis->scene()->items().size(); ++i tokens.size() > 0 CVariableDeclaration* new_var = seq->insertNewAfter (vd); QString rest; m->setPos( 600 , 0 ); sections.addItem(&sec_protected); qDebug() << m->getName().getText(); style SBox* sections.addItem(&sec_private); break; vis->showPrompt(event->pos().x(), event->pos().y()); event->ignore(); VModule* mod = dynamic_cast (vis->scene()->items()[i]); tokens[0] == "module" VBase::focus(&new_var->getName()); getLoopIfElseHeader(lines.first(), header, rest); module_name == "handlers" && class_name == "HBase" && method_name == "~HBase" bool in_string = false; default_style static SBox case Qt::Key_Space: mod vis->getCursor().pos = 0; CIfElse* ife = s.insertNewAfter (); VBox::updateRepresentation(); event vis CApplication* app = dynamic_cast (vis->getConstruct()); QChar match; m->setPos( 200 , 0 ); painter option widget keySpace(event, vis); keyControl ife->getHeader().setText(header); paint QKeyEvent* VBase* mod->visualizationChanged(); CModule* cmod = app->getModules().insertNew(); parent cons getClientWidth client QPainter* const QStyleOptionGraphicsItem* QWidget* break; lines[0] = rest; QChar val; VBox setClient module_name == "handlers" && class_name == "HBase" && method_name == "setView" VBase* Construct* VBase* case Qt::Key_Backspace: event->ignore(); tokens.size() > 1 lines.first().length() < 1 int level = 1; return client_width; VBox::paint(painter, option, widget); event vis m->setPos( 1000 , 0 ); keyDelBackspace(event, vis); event->key() == Qt::Key_9 cmod->getName().setText(tokens[1]); keyControl header = ""; setFlag(ItemIsMovable); this->client = client; QPen pen(Qt::black); QKeyEvent* VBase* lines.removeFirst(); break; rest = ""; module_name == "handlers" && class_name == "HBase" && method_name == "view" setFlag(ItemIsFocusable); getClientHeight updateRepresentation(); QVector dashes; int i = 0; i < vis->scene()->items().size(); ++i vis->hasPrompt() VMethodArgument* ma = dynamic_cast (vis); case Qt::Key_Delete: event vis parseBody(ife->getThen(), lines); int i = 1; i < line.size(); ++i setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); dashes << 5 << 10; keyAlt m->setPos( 600 , 100 ); return client_height; keyDelBackspace(event, vis); QKeyEvent* VBase* VMethod* vm = dynamic_cast (vis->scene()->items()[i]); cmod->setPos(vis->requestPrompt()->x(), vis->requestPrompt()->y()); ma->getCursor().pos = 0; lines.first() == "else" setZValue(5); header pen.setDashPattern(dashes); val = line[i]; setHeader break; vm ! ma->getName().HasFocusInHierarchy() module_name == "handlers" && class_name == "HBase" && method_name == "mouseMoveEvent" client_width = 0; VBase* pen.setWidth(1); event->ignore(); VBase::setConsWithPrompt(cmod); lines.removeFirst(); in_string && val == match && line[i - 1] != SLASH getPadding case Qt::Key_Tab ... Qt::Key_Backtab: CMethod* cm = dynamic_cast (vm->getConstruct()); ma->getName().setFocus(); ma->getType().setFocus(); m->setPos( 1300 , 250 ); client_height = 0; painter->setPen(pen); processed = true; parseBody(ife->getElse(), lines); this->header = header; keyTab(event, vis); in_string = false; !in_string && (val == '\'' || val == '"') header_width = 0; return style->padding_top; painter->drawLine(sections.pos().x() + sec_protected.pos().x() - DISTANCE / 2, sections.pos().y(), sections.pos().x()+ sec_protected.pos().x() - DISTANCE / 2, sections.pos().y() + sections.boundingRect().height()); cm->getName().getText() == "setStyle" qDebug() << "OUT parseIfElse " << lines.first(); module_name == "handlers" && class_name == "HBase" && method_name == "mouseReleaseEvent" updateRepresentation(); break; return processed; header_height = 0; painter->drawLine(sections.pos().x() + sec_private.pos().x() - DISTANCE / 2, sections.pos().y(),sections.pos().x() + sec_private.pos().x() - DISTANCE / 2, sections.pos().y()+ sections.boundingRect().height()); event vis in_string = true; !in_string && val == '(' case Qt::Key_Shift: keyDelBackspace static bool err = false; m->setPos( 1300 , 500 ); icon_width = 0; QKeyEvent* VBase* err = !err; match = val; getStyle icon keyShift(event, vis); ++level; !in_string && val == ')' icon_height = 0; setIcon module_name == "handlers" && class_name == "HBase" && method_name == "keyPressEvent" VBase* err return style; VMethod VMethodArgument MethodIcon break; event->ignore(); HSequence s lines --level; updateRepresentation(); parseLoop m->setPos( 0 , 300 ); this->icon = icon; case Qt::Key_Control: vm->setStyle(SMethod::getErrorStyle()); (vm->setStyle(SMethod::getNormalStyle())); CSequence& QStringList& method CMethod* dec CVariableDeclaration* the_instance static HSequence keyControl(event, vis); updateRepresentation(); vm->visualizationChanged(); qDebug() << "IN parseLoop " << lines.first(); module_name == "handlers" && class_name == "HClass" && method_name == "HClass" header VLinear v VLinear parent event vis level > 0 MethodIcon break; keyDigit icon MethodIcon name VText VBase* QKeyEvent* VBase* HSequence getInstance lines.removeFirst(); m->setPos( 17 , 1 ); style case Qt::Key_Alt: header.append(val); rest = line.right(line.length() - i - 1).simplified(); setStyle name VBoxed type VText QString header; SBox* updateRepresentation(); keyAlt(event, vis); keyLetter(event, vis); event->key() == Qt::Key_8 return &the_instance; break; module_name == "handlers" && class_name == "HClass" && method_name == "command" statements VSequence QString rest; break; style = style_; arguments VSequence VMethodArgument parent dec getName getLoopIfElseHeader(lines.first(), header, rest); m->setPos( 850 , 150 ); VMethodArgument case Qt::Key_Left: int i = 0; i < vis->scene()->items().size(); ++i header = header.simplified(); updateRepresentation(); VBase* CVariableDeclaration* CLoop* loop = s.insertNewAfter (); return name; keyArrow(event, vis); event vis VMethod* vm = dynamic_cast (vis->scene()->items()[i]); module_name == "handlers" && class_name == "HClass" && method_name == "getInstance" parent method getStatements keyLetter loop->getHeader().setText(header); VMethod setEventHandler( HMethodArgument::getInstance() ); break; QKeyEvent* VBase* event vis m->setPos( 450 , 0 ); width height VBase* CMethod* vm keyArrow lines[0] = rest; setClientSize return statements; name.setZValue(v.zValue() + 1); getType case Qt::Key_Up: QKeyEvent* VBase* int int event->ignore(); vm->setStyle(SMethod::getGrayStyle()); lines.first().length() < 1 arguments.setSequenceOptions(false, 5); type.setZValue(v.zValue() + 1); keyArrow(event, vis); module_name == "handlers" && class_name == "HClassField" && method_name == "keyEnter" return type; CMethod* cm = dynamic_cast (vm->getConstruct()); VSequence* st = dynamic_cast*> (vis); width >= 0 name.item.setStyle(SMethodName::getNormalStyle()); type.setStyle(SType::getNormalStyle()); updateRepresentation break; lines.removeFirst(); m->setPos( 0 , 100 ); cm->getName().getText() == "setStyle" st updateRepresentation name.setStyle(SMethod::getTitleStyle()); v.setOptions(true, true, 2); bounding_rect = QRectF(0, 0, SIZE, SIZE); case Qt::Key_Right: client_width = width; event vis parseBody(loop->getBody(), lines); module_name == "handlers" && class_name == "HClassField" && method_name == "keyControl" arguments.setStyle(SMethod::getTitleStyle()); v.addItem(&name); keyArrow(event, vis); keyOther vm->setStyle(SMethod::getHighlightStyle()); event->key() == Qt::Key_Up client QKeyEvent* VBase* qDebug() << "OUT parseLoop " << lines.first(); height >= 0 setStyle(SMethod::getNormalStyle()); v.addItem(&type); break; m->setPos( 0 , 400 ); painter option widget str (*st->getItems().begin())->HasFocusInHierarchy() client_width = client->boundingRect().width(); paint tokenize cm->getName().getText() == "keyDigit" client_height = height; header.setOptions(false, true, 15); setStyle(SInvisible::getNormalStyle()); QPainter* const QStyleOptionGraphicsItem* QWidget* case Qt::Key_Down: event->text().size() > 0 QString module_name == "handlers" && class_name == "HClassField" && method_name == "getInstance" client_height = client->boundingRect().height(); header.addItem(&name); setClient(&v); keyArrow(event, vis); vm->setStyle(SMethod::getSourceStyle()); event->ignore(); int i = st->size() -1; i > 0; --i keyLetter(event, vis); return; m->setPos( 900 , 0 ); header.addItem(&arguments); QPainterPath p; break; str = str.simplified(); cm->getName().getText() == "updateRepresentation" header int step = 360 / (2 * NUM_TEETH); int i = 0; st->getItems()[i]->HasFocusInHierarchy() setClient(&statements); default: module_name == "handlers" && class_name == "HGlobal" && method_name == "HGlobal" header_width = header->boundingRect().width(); setHeader(&header); int i = 0; keyOther(event, vis); i < str.size() vm->setStyle(SMethod::getDestinationStyle()); st->getItems()[i - 1]->setFocus(); m->setPos( 20 , 1 ); header_height = header->boundingRect().height(); setIcon(&icon); int degree = step / 2; degree < 360; degree += step } vm->visualizationChanged(); st->getItems()[i - 1]->visualizationChanged(); false == (str[i].isDigit() || str[i].isLetter() || str[i].isSpace()) return; module_name == "handlers" && class_name == "HGlobal" && method_name == "command" icon int len; event->isAccepted() str.insert(i + 1, ' '); icon_width = icon->boundingRect().width(); float x, y; QApplication::postEvent(view(), new UpdateEvent()); event->key() == Qt::Key_1 m->setPos( 800 , 50 ); icon_height = icon->boundingRect().height(); len = (i % 2) ? MID_RADIUS : OUT_RADIUS; i++; event->key() == Qt::Key_Down int i = 0; i < vis->scene()->items().size(); ++i module_name == "handlers" && class_name == "HGlobal" && method_name == "getInstance" x = SIZE / 2 + len * cos(degree * (PI / 180.0)); int max_width = std::max(client_width, header_width + icon_width + style->icon_distance - style->padding_left); return str.split(' ', QString::SkipEmptyParts); (*st->getItems().rbegin())->HasFocusInHierarchy() y = SIZE / 2 + len * sin(degree * (PI / 180.0)); VMethod* vm = dynamic_cast (vis->scene()->items()[i]); m->setPos( 400 , 0 ); outter_width = std::max(max_width + style->padding_left + style->padding_right, 2 * style->corner_radius); event->ignore(); degree == step / 2 vm int i = 0; i < st->size(); ++i module_name == "handlers" && class_name == "HIfElse" && method_name == "keyControl" int header_icon_height = std::max(header_height, icon_height); return; int top_distance = std::max(style->padding_top, header_icon_height / 2 + style->header_distance); p.moveTo(x, y); p.lineTo(x, y); vm->setStyle(SMethod::getNormalStyle()); st->getItems()[i]->HasFocusInHierarchy() m->setPos( 0 , 200 ); outter_height = std::max(top_distance + client_height + style->padding_bottom, 2 * style->corner_radius); vm->visualizationChanged(); len = (i % 2) ? OUT_RADIUS : MID_RADIUS; st->getItems()[i + 1]->setFocus(); module_name == "handlers" && class_name == "HIfElse" && method_name == "getInstance" int adjust_x = style->border.width() + style->shadow_x; x = SIZE / 2 + len * cos(degree * (PI / 180.0)); st->getItems()[i + 1]->visualizationChanged(); int adjust_y = style->border.width() + style->shadow_y; m->setPos( 500 , 0 ); VStatement y = SIZE / 2 + len * sin(degree * (PI / 180.0)); HText event->key() == Qt::Key_2 return; bounding_rect = QRectF(0, 0, outter_width + adjust_x, outter_height + adjust_y + (int) (header_icon_height / 2)); p.lineTo(x, y); static VStackTrace* vst = NULL; module_name == "handlers" && class_name == "HMethodArgument" && method_name == "keyEnter" statement CStatement* the_instance static HText icon i = (i + 1); text VText vst m->setPos( 0 , 200 ); icon->setPos(0, (header_icon_height - icon_height) / 2); p.closeSubpath(); HText getInstance vis->scene()->removeItem(vst); vst = new VStackTrace(NULL); HStatement module_name == "handlers" && class_name == "HMethodArgument" && method_name == "keyControl" header parent statement getText p.addEllipse(SIZE / 2 - IN_RADIUS, SIZE / 2- IN_RADIUS, 2 * IN_RADIUS, 2 * IN_RADIUS); delete vst; CApplication* app = dynamic_cast (vis->getConstruct()); VStatement return &the_instance; the_instance static HStatement m->setPos( 0 , 500 ); VBase* CStatement* QPen pen(Qt::black); vst = NULL; vst->addMethod(app->getModule("vis")->getClass("VBase")->getMethod("keyPressEvent"), 1525, 0); style->header_centered return text; painter->setBrush(Qt::gray); vst->addVarInfo(app->getModule("vis")->getClass("VBase")->getMethod("keyPressEvent")->getArguments()[0],"Key='2'"); module_name == "handlers" && class_name == "HMethodArgument" && method_name == "getInstance" setEventHandler( HStatement::getInstance() ); HStatement getInstance header->setPos(icon_width + (bounding_rect.width() - icon_width - header_width) / 2,(header_icon_height - header_height) / 2); header->setPos(icon_width + style->icon_distance, (header_icon_height - header_height) / 2); pen.setWidth(1); vst->addMethod(app->getModule("handlers")->getClass("HBase")->getMethod("keyPressEvent"), 1350, 6); setStyle(SInvisible::getNormalStyle()); m->setPos( 950 , 0 ); painter->setPen(pen); return &the_instance; setClient(&text); vst->addVarInfo(app->getModule("handlers")->getClass("HBase")->getMethod("keyPressEvent")->getArguments()[0],"Key='2'"); client painter->scale(0.6, 0.6); module_name == "handlers" && class_name == "HModule" && method_name == "HModule" setFocusProxy(&text); vst->addVarInfo(app->getModule("handlers")->getClass("HBase")->getMethod("keyPressEvent")->getArguments()[1],"App name=\"Envision\""); painter->drawPath(p); client->setPos(style->padding_left, header_icon_height / 2 + top_distance); event vis event vis vst->addMethod(app->getModule("handlers")->getClass("HApplication")->getMethod("keyDigit"), 0, 26); m->setPos( 17 , 1 ); painter option widget keyDelBackspace buttonRelease paint painter->translate(SIZE/2 + 5,SIZE/2 + 5); QKeyEvent* VBase* QGraphicsSceneMouseEvent* VBase* vst->addVarInfo(app->getModule("handlers")->getClass("HApplication")->getMethod("keyDigit")->getArguments()[0],"Key='2'"); QPainter* const QStyleOptionGraphicsItem* QWidget* painter->drawPath(p); vst->addVarInfo(app->getModule("handlers")->getClass("HApplication")->getMethod("keyDigit")->getArguments()[1],"App name=\"Envision\""); event vis module_name == "handlers" && class_name == "HModule" && method_name == "command" Cursor& cursor = vis->getCursor(); CText* text = dynamic_cast (vis->getConstruct()); keyEnter int half_header = std::max(header_height,icon_height) / 2; vst->addMethod(app->getModule("vis")->getClass("VStackTrace")->getMethod("addMethod"), 1170, 10); QKeyEvent* VBase* m->setPos( 850 , 100 ); VLoop LoopIcon CText* text = dynamic_cast (vis->getConstruct()); int pos = event->pos().x(); style->shadow_x > 0 || style->shadow_y > 0 vst->addVarInfo(app->getModule("vis")->getClass("VStackTrace")->getMethod("addMethod")->getArguments()[0],"Name=\"KeyPressEvent\""); VStatement* vs = dynamic_cast (vis); switch (event->key()) vis->boundingRect().width() == 0 || text->getText().length() == 0 module_name == "handlers" && class_name == "HModule" && method_name == "getInstance" vst->addVarInfo(app->getModule("vis")->getClass("VStackTrace")->getMethod("addMethod")->getArguments()[1],"1470"); painter->setPen(Qt::NoPen); loop CLoop* { CStatement* st = dynamic_cast (vs->getConstruct()); vis->getCursor().pos = 0; vis->getCursor().pos = pos / (vis->boundingRect().width() / text->getText().length()); vst->addVarInfo(app->getModule("vis")->getClass("VStackTrace")->getMethod("addMethod")->getArguments()[2], "0"); m->setPos( 450 , 0 ); painter->setBrush(style->shadow); icon LoopIcon parent case Qt::Key_Backspace: CSequence* cs = dynamic_cast*> (st->getParent()); LoopIcon vst->addMethod(app->getModule("vis")->getClass("VLinear")->getMethod("addItem"), 1450, 1); painter->drawPath(roundedRect(style->shadow_x, half_header + style->shadow_y, outter_width+ style->border.width(), outter_height + style->border.width(), style->corner_radius, style->cut_corners)); header VBoxed VBase* { adjustCursor(vis); vis->scene()->clearFocus(); module_name == "handlers" && class_name == "HPrompt" && method_name == "HPrompt" vst->addVarInfo(app->getModule("vis")->getClass("VLinear")->getMethod("addItem")->getArguments()[0],"Item type = VSpacer(10,1680)"); body VSequence cursor.pos--; st->getText().getText().startsWith("if ") painter->setBrush(style->background); updateRepresentation(); vst->addMethod(app->getModule("vis")->getClass("VLinear")->getMethod("updateRepresentation"), 900, 15); m->setPos( 15 , 9 ); painter->setPen(style->border); cursor.pos >= 0 CIfElse* ifelse = cs->insertNewAfter (st); st->getText().getText().startsWith("for ") || st->getText().getText().startsWith("while ") parent loop getBody vst->setPos(0, 0); module_name == "handlers" && class_name == "HPrompt" && method_name == "keyEnter" VLoop ifelse->getHeader().setText(st->getText().getText().mid(3)); int half_border = (style->border.width() + 1) / 2; VBase* CLoop* text->setText(text->getText().remove(cursor.pos, 1)); cursor.pos = 0; vis->scene()->addItem(vst); HModule CLoop* loop = cs->insertNewAfter (st); CStatement* ns = cs->insertNewAfter (st); painter->drawPath(roundedRect(half_border, half_header + half_border, outter_width, outter_height,style->corner_radius, style->cut_corners)); return body; CStatement* ns = ifelse->getThen().insertNewAfter (); m->setPos( 3 , 139 ); } QString rest = st->getText().getText().mid(st->getText().getText().indexOf(' ') + 1); VBase::focus(&ns->getText()); header.setStyle(SLoop::getHeaderStyle()); the_instance static HModule VBase::focus(&ns->getText()); x y width height radius cut_edges change &value st->getText().getText().startsWith("for ") && st->getText().getText().simplified().split(' ').count() == 2 module_name == "handlers" && class_name == "HPrompt" && method_name == "buttonRelease" roundedRect itemChange setStyle(SLoop::getNormalStyle()); updateRepresentation break; int int int int int bool GraphicsItemChange const QVariant cs->remove(st); setClient(&body); case Qt::Key_Delete: HModule getInstance loop->getHeader().setText("int i = 0; i<" + rest + "; ++i"); loop->getHeader().setText(rest); m->setPos( 850 , 100 ); bounding_rect = QRectF(0, 0, BOUNDARY, BOUNDARY); QPainterPath path; { change == ItemPositionChange && style->snap_to_grid > 0 setHeader(&header); return &the_instance; CStatement* ns = loop->getBody().insertNewAfter (); module_name == "handlers" && class_name == "HPrompt" && method_name == "getInstance" path.moveTo(width + x, radius + y); setIcon(&icon); cursor.pos < text->getText().length() QPointF newPos = value.toPointF(); painter option widget VBase::focus(&ns->getText()); m->setPos( 450 , 0 ); cut_edges setFocusProxy( &body ); paint text->setText(text->getText().remove(cursor.pos, 1)); newPos.setX(((int) (newPos.x() / style->snap_to_grid)) * style->snap_to_grid); QPainter* const QStyleOptionGraphicsItem* QWidget* tokens vis cs->remove(st); command module_name == "handlers" && class_name == "HSequence" && method_name == "keyArrow" path.lineTo(width + x - radius, y); path.arcTo(width - 2 * radius + x, y, radius * 2, radius * 2, 0.0, 90.0); newPos.setY(((int) (newPos.y() / style->snap_to_grid)) * style->snap_to_grid); } QStringList VBase* QPainterPath p; vis->getCursor().pos = 0; return newPos; break; m->setPos( 0 , 150 ); path.lineTo(radius + x, y); p.moveTo(SIZE - ARROW_WING - 1, SIZE / 2 - 1); bool processed = false; } cut_edges return QGraphicsItem::itemChange(change, value); p.arcTo(ARROW_WING, 1, SIZE - ARROW_WING - 1, SIZE-2, 5, 150); tokens.size() > 0 HIfElse module_name == "handlers" && class_name == "HSequence" && method_name == "getInstance" adjustCursor(vis); path.lineTo(x, radius + y); path.arcTo(x, y, radius * 2, radius * 2, 90.0, 90.0); p.lineTo(1, SIZE/2 - (4*ARROW_WING)/3 - 1); tokens[0] == "class" the_instance static HIfElse m->setPos( 950 , 0 ); p.lineTo(2*ARROW_WING+1, SIZE/2 - 1); path.lineTo(x, height - radius + y); CModule* mod = dynamic_cast (vis->getConstruct()); painter option widget p.lineTo(4*ARROW_WING+1, SIZE/2 - (4*ARROW_WING)/3 - 1); module_name == "handlers" && class_name == "HStatement" && method_name == "HStatement" paint event vis CClass* cc = mod->getClasses().insertNew(); HIfElse getInstance cut_edges QPainter* const QStyleOptionGraphicsItem* QWidget* p.arcTo(3*ARROW_WING - ARROW_WING/3 - 1, ARROW_WING, SIZE - 4*ARROW_WING , SIZE - ARROW_WING, 143, -130); keyLetter m->setPos( 5 , 9 ); QKeyEvent* VBase* path.lineTo(x + radius, height + y); path.arcTo(x, height - 2 * radius + y, radius * 2, radius * 2, 180.0, 90.0); p.closeSubpath(); tokens.size() > 1 return &the_instance; VStatement::paint(painter, option, widget); module_name == "handlers" && class_name == "HStatement" && method_name == "keyEnter" QLinearGradient gradient(0,0,20,20); vis->getCursor().pos++; path.lineTo(width - radius + x, height + y); QPoint t[3]; cc->getName().setText(tokens[1]); gradient.setColorAt(0, QColor(Qt::white)); CText* text = dynamic_cast (vis->getConstruct()); m->setPos( 6 , 178 ); cut_edges painter->setBrush(getStyle()->border.color()); vis->hasPrompt() gradient.setColorAt(1, QColor(0x80,0x80,0xFF)); text->setText(text->getText().insert(vis->getCursor().pos - 1, event->text())); painter->setPen(Qt::NoPen); module_name == "handlers" && class_name == "HStatement" && method_name == "getInstance" path.lineTo(x + width, height - radius + y); path.arcTo(width - 2 * radius + x, height - 2 * radius + y, radius * 2, radius * 2, 270.0, 90.0); painter->setBrush( QBrush(gradient) ); adjustCursor(vis); vis cc->setPos(vis->requestPrompt()->x(), vis->requestPrompt()->y()); const int RIGHT_OFFSET = 1; adjustCursor VBase* m->setPos( 450 , 0 ); path.closeSubpath(); QPen pen(Qt::black, 1); VBase::setConsWithPrompt(cc); int i = bounding_rect.bottom() - getClientHeight(); i < bounding_rect.bottom() - getPadding(); i += 5* TRIANGLE_SIDE event vis return path; painter->setPen(pen); keyControl vis->getCursor().pos < 0 processed = true; QKeyEvent* VBase* module_name == "handlers" && class_name == "HText" && method_name == "adjustCursor" t[0] = QPoint(RIGHT_OFFSET, i); painter->drawPath(p); event vis m->setPos( 1650 , 1050 ); t[1] = QPoint(+TRIANGLE_SIDE / 2 + RIGHT_OFFSET, i); painter->rotate(180); keyArrow vis->getCursor().pos = 0; QKeyEvent* VBase* return processed; VIfElse* v = dynamic_cast (vis); t[2] = QPoint(RIGHT_OFFSET, i + 2 * TRIANGLE_SIDE); painter->translate(-SIZE - 6, -SIZE); CText* text = dynamic_cast (vis->getConstruct()); HClass v->switchFocus(); module_name == "handlers" && class_name == "HText" && method_name == "HText" painter->drawPolygon(t, 3); painter->drawPath(p); switch (event->key()) vis->getCursor().pos > text->getText().length() event->accept(); m->setPos( 15 , -9 ); { the_instance static HClass vis->getCursor().pos = text->getText().length(); int i = bounding_rect.left() + 2 * getPadding(); i < bounding_rect.right() - getPadding(); i += 5* TRIANGLE_SIDE case Qt::Key_Left: module_name == "handlers" && class_name == "HText" && method_name == "keyLetter" HClass getInstance t[0] = QPoint(i, bounding_rect.bottom()); vis->getCursor().pos--; vis->visualizationChanged(); m->setPos( 0 , 900 ); break; t[1] = QPoint(i, bounding_rect.bottom() - TRIANGLE_SIDE / 2); return &the_instance; t[2] = QPoint(i + 2 * TRIANGLE_SIDE, bounding_rect.bottom()); case Qt::Key_Right: module_name == "handlers" && class_name == "HText" && method_name == "keyArrow" painter->drawPolygon(t, 3); vis->getCursor().pos++; m->setPos( 0 , 1150 ); VGrid break; int i = bounding_rect.bottom() - 2 * getPadding(); i > bounding_rect.bottom() - getClientHeight(); i -= 5* TRIANGLE_SIDE module_name == "handlers" && class_name == "HText" && method_name == "keyDelBackspace" items std::vector< std::vector< VBase* > > default: tokens vis command row_heights std::vector< int > t[0] = QPoint(bounding_rect.right() - TRIANGLE_SIDE / 2, i); { QStringList VBase* m->setPos( 0 , 200 ); column_widths std::vector< int > t[1] = QPoint(bounding_rect.right(), i); event->ignore(); bool processed = false; module_name == "handlers" && class_name == "HText" && method_name == "buttonRelease" size_x int t[2] = QPoint(bounding_rect.right(), i - 2 * TRIANGLE_SIDE); return; tokens.size() > 0 m->setPos( 1200 , 200 ); size_y int painter->drawPolygon(t, 3); } } tokens[0] == "method" module_name == "handlers" && class_name == "HText" && method_name == "getInstance" parent ~VGrid getY new_x new_y int i = bounding_rect.right() - 2 * getPadding(); i > bounding_rect.left() + 2 * getPadding()+ header.boundingRect().width(); i -= 5 * TRIANGLE_SIDE adjustCursor(vis); VGrid setXY CClass* cc = dynamic_cast (vis->getConstruct()); m->setPos( 850 , 0 ); VBase* int int tokens[0] == "field" removeAllItems(); return size_y; t[0] = QPoint(i, bounding_rect.bottom() - getClientHeight() - header.boundingRect().height() / 2 - 2* getPadding() + TRIANGLE_SIDE / 2); CMethod* m = cc->getMethods().insertNew(); CClass* cc = dynamic_cast (vis->getConstruct()); module_name == "handlers" && class_name == "UpdateEvent" && method_name == "UpdateEvent" setClientSize(5,5); removeAllItems(); t[1] = QPoint(i, bounding_rect.bottom() - getClientHeight() - header.boundingRect().height() / 2 - 2* getPadding()); CStatement* s = m->getStatements().insertNewAfter (NULL); CVariableDeclaration* vd = cc->getFields().insertNew(); getX column_widths.clear(); t[2] = QPoint(i - 2 * TRIANGLE_SIDE, bounding_rect.bottom() - getClientHeight()- header.boundingRect().height() / 2 - 2 * getPadding()); HPrompt int next = 1; m->setPos( 250 , 0 ); painter->drawPolygon(t, 3); int next = 1; row_heights.clear(); tokens.size() > next return size_x; the_instance static HPrompt module_name == "parsers" && class_name == "Parser" && method_name == "parseDir" removeAllItems x y tokens.size() > next removeItem new_x <= 0 || new_y <= 0 int int tokens[next] == "private" m->setPos( 0 , 750 ); HPrompt getInstance tokens[next] == "private" int y = 0; y < size_y; ++y size_x = 0; size_x = new_x; VIfElse IfElseIcon m->setVisibility(Visibility::PRIVATE); tokens[next] == "protected" x>= 0 && x < size_x && y >= 0 && y < size_y && items[y][x] size_y = 0; size_y = new_y; vd->setVisibility(Visibility::PRIVATE); tokens[next] == "protected" module_name == "parsers" && class_name == "Parser" && method_name == "parseFile" ifelse CIfElse* return &the_instance; ++next; int x = 0; x < size_x; ++x column_widths.resize(size_x); m->setVisibility(Visibility::PROTECTED); tokens[next] == "public" ++next; m->setPos( 0 , 1700 ); delete items[y][x]; icon IfElseIcon parent vd->setVisibility(Visibility::PROTECTED); tokens[next] == "public" row_heights.resize(size_y); IfElseIcon ++next; items[y][x] items[y][x] = NULL; header VBoxed VBase* event vis event vis m->setVisibility(Visibility::PUBLIC); ++next; items.resize(size_y); keyEnter buttonRelease vd->setVisibility(Visibility::PUBLIC); module_name == "parsers" && class_name == "Parser" && method_name == "removeComments" delete items[y][x]; updateRepresentation(); body VLinear > QKeyEvent* VBase* QGraphicsSceneMouseEvent* VBase* ++next; updateRepresentation(); ++next; m->setPos( 0 , 2950 ); int y = 0; y items[y][x] = NULL; CPrompt* p = dynamic_cast (vis->getConstruct()); event->accept(); items[y].resize(size_x); _branch _branch module_name == "parsers" && class_name == "Parser" && method_name == "putOnNewLine" processCommand(p->getText().getText(), vis); tokens.size() > next tokens.size() > next updateRepresentation(); int x = 0; xaccept(); m->setPos( 3200 , 2950 ); parent ifelse getThen m->getName().setText(tokens[next]); VIfElse vd->getName().setText(tokens[next]); items[y][x] = NULL; VBase* CIfElse* ++next; module_name == "parsers" && class_name == "Parser" && method_name == "normalizeParenthesis" return then_branch; updateRepresentation ++next; m->setPos( 2400 , 2950 ); updateRepresentation setEventHandler(HIfElse::getInstance()); CVariableDeclaration* arg = NULL; updateRepresentation(); bounding_rect = QRectF(0, 0, SIZE, SIZE); ; next < tokens.size(); ++next then_branch.setStyle(SIfElse::getThenStyle()); getElse ; next < tokens.size(); ++next module_name == "parsers" && class_name == "Parser" && method_name == "getLoopIfElseHeader" int x = 0; x < size_x; ++x x y item else vd->getType().setText(vd->getType().getText() + tokens[next]); setItem return painter option widget tokens[next] == "," m->setPos( 650 , 8500 ); column_widths[x] = 0; int int VBase* _branch.setStyle(SIfElse::getElseStyle()); paint tokens.size() - 1 > next else QPainter* const QStyleOptionGraphicsItem* QWidget* body.setStyle(SInvisible::getNormalStyle()); arg = NULL; module_name == "parsers" && class_name == "Parser" && method_name == "parseFileEntry" removeItem(x, y); _branch; vd->getType().setText(vd->getType().getText() + " "); int y = 0; y < size_y; ++y setStyle(SIfElse::getNormalStyle()); QPainterPath p; continue; x>= 0 && x < size_x && y >= 0 && y < size_y m->setPos( 0 , 3800 ); p.moveTo(SIZE/2 , 1); row_heights[y] = 0; header.setStyle(SIfElse::getHeaderStyle()); vis->removePrompt(); items[y][x] = item; arg == NULL body.setOptions(false, false, 15); p.lineTo(1, SIZE/2); vis->getCursor().pos = 0; UpdateEvent module_name == "parsers" && class_name == "Parser" && method_name == "parseClassDeclaration" updateRepresentation(); arg = m->getArguments().insertNewAfter (); int y = 0; y < size_y; ++y body.addItem(&then_branch); p.lineTo(SIZE/2, SIZE - 1); arg->getType().getText().size() > 0 VBase::focus(&vd->getName()); m->setPos( 0 , 4850 ); arg->getName().setText(tokens[next]); body.addItem(&else_branch); p.lineTo(SIZE - 1, SIZE/2); arg->getType().setText(arg->getType().getText() + " "); processed = true; int x = 0; x < size_x; ++x setClient(&body); p.closeSubpath(); module_name == "parsers" && class_name == "Parser" && method_name == "parseMethodArguments" arg->getType().setText(arg->getType().getText() + tokens[next]); UpdateEvent setHeader(&header); QLinearGradient gradient(5,5,20,20); = m->setPos( 0 , 7450 ); items[y][x] QEvent::Type setIcon(&icon); gradient.setColorAt(0, Qt::white); row_heights[y] < items[y][x]->boundingRect().height() vis->removePrompt(); module_name == "parsers" && class_name == "Parser" && method_name == "parseBody" setFocusProxy(&then_branch); gradient.setColorAt(1, Qt::yellow); vis->getCursor().pos = 0; row_heights[y] = items[y][x]->boundingRect().height(); painter->setBrush( QBrush(gradient) ); m->setPos( 0 , 6500 ); VBase::focus(&s->getText()); QPen pen(Qt::black, 1); column_widths[x] < items[y][x]->boundingRect().width() processed = true; module_name == "parsers" && class_name == "Parser" && method_name == "parseLoop" painter->setPen(pen); column_widths[x] = items[y][x]->boundingRect().width(); m->setPos( 0 , 9100 ); painter->drawPath(p); return processed; module_name == "parsers" && class_name == "Parser" && method_name == "parseIfElse" int total_width = 0; m->setPos( 0 , 8500 ); int total_height = 0; switchFocus module_name == "parsers" && class_name == "Parser" && method_name == "createModule" int x = 0; x < size_x; ++x cursor.pos = 0; m->setPos( 5450 , -50 ); total_width += column_widths[x]; then_branch.HasFocusInHierarchy() module_name == "parsers" && class_name == "Parser" && method_name == "createClass" int y = 0; y < size_y; ++y else_branch.size() < 1 then_branch[0]->getText().setFocus(); m->setPos( 5950 , -50 ); total_height += row_heights[y]; CSequence* seq = dynamic_cast*> (else_branch.getConstruct()); else module_name == "parsers" && class_name == "Parser" && method_name == "createMethod" seq->insertNewAfter (); _branch[0]->getText().setFocus(); setClientSize(total_width, total_height); focus(&(*seq)[0]->getText()); m->setPos( 6750 , -50 ); total_height = 0; qDebug() << "Scheduled for focusing"; int y = 0; y < size_y; ++y module_name == "parsers" && class_name == "Parser" && method_name == "Parser" total_width = 0; m->setPos( 0 , 50 ); int x = 0; x < size_x; ++x module_name == "parsers" && class_name == "Parser" && method_name == "~Parser" items[y][x] m->setPos( 200 , 50 ); items[y][x]->setPos( getPadding() + total_width + ceil(( column_widths[x] - items[y][x]->boundingRect().width())/2) ,getPadding() + total_height + ceil(( row_heights[y] - items[y][x]->boundingRect().height())/2) ) ; module_name == "parsers" && class_name == "Parser" && method_name == "getApp" total_width += column_widths[x]; m->setPos( 0 , 400 ); total_height += row_heights[y]; module_name == "styles" && class_name == "SApplication" && method_name == "getNormalStyle"

VBox::updateRepresentation(); m->setPos( 0 , 0 ); module_name == "styles" && class_name == "SApplication" && method_name == "getErrorStyle" m->setPos( 0 , 350 );

module_name == "styles" && class_name == "SBox" && method_name == "SBox" m->setPos( 7 , 5 );

VSet VSequence VLinear module_name == "styles" && class_name == "SBox" && method_name == "~SBox" cons CSet* cons CSequence* items std::vector m->setPos( 200 , 0 ); items std::vector items std::vector distance int module_name == "styles" && class_name == "SBox" && method_name == "setPadding" distance int distance int vertical bool vertical bool justified bool m->setPos( 0 , 350 ); parent construct ~VSet VSet module_name == "styles" && class_name == "SBox" && method_name == "setPadding" VBase* CSet* parent construct ~VSequence getItems vertical distance parent size vertical justified distance removeAllItems(); VSequence setSequenceOptions VLinear setOptions VBase* CSequence* bool int VBase* bool bool int m->setPos( 0 , 350 ); setFlag(ItemIsMovable, false); removeAllItems(); return items; return items.size(); setStyle(SInvisible::getNormalStyle()); setEventHandler(HSequence::getInstance()); this->vertical = vertical; setFlag(ItemIsMovable, false); this->vertical = vertical; module_name == "styles" && class_name == "SBox" && method_name == "setShadowSize" updateRepresentation(); setFlag(ItemIsMovable, false); size this->distance = distance; setStyle(SInvisible::getNormalStyle()); this->justified = justified; m->setPos( 0 , 1150 ); setStyle(SInvisible::getNormalStyle()); updateRepresentation(); updateRepresentation(); this->distance = distance; return items.size(); module_name == "styles" && class_name == "SBox" && method_name == "setTransparentBackground" updateRepresentation(); updateRepresentation(); m->setPos( 0 , 700 ); i operator[] int VText VPrompt VDummy module_name == "styles" && class_name == "SBox" && method_name == "setFadeBackground" m->setPos( 0 , 100 ); return items[i]; text CText* prompt CPrompt* construct T* cursor_x int contents VLinear module_name == "styles" && class_name == "SBox" && method_name == "setCorners" removeAllItems 2 static const int EXTRA_TEXT_SPACE = text VText parent cons get VDummy m->setPos( 0 , 1000 ); style SText* errors VLinear VBase* T* return construct; size_t i = 0; i < items.size(); ++i default_style static SText error1 VText* bounding_rect.setCoords(0,0,0,0); module_name == "styles" && class_name == "SBox" && method_name == "setSnapToGrid" updateRepresentation updateRepresentation error2 VText* delete items[i]; m->setPos( 0 , 800 ); parent text style items[i] = NULL; isConstructIdentityChanged() int max_height = 0; VText setStyle VBase* CText* SText* parent prompt updateRepresentation module_name == "styles" && class_name == "SClass" && method_name == "getNormalStyle" int max_width = 0; VPrompt items.clear(); removeAllItems(); VBase* CPrompt* int total_width = 0; setEventHandler(HText::getInstance()); this->style = style; errors.size() > 0 && prompt->getError1().getText().isEmpty() m->setPos( 33 , 9 ); updateRepresentation size_t i = 0; i < cons->size(); ++i int total_height = 0; setFlag(ItemIsMovable); updateRepresentation(); setEventHandler(HPrompt::getInstance()); updateRepresentation errors.removeAllItems(); module_name == "styles" && class_name == "SClass" && method_name == "getErrorStyle" C* item = (*cons)[i]; size_t i = 0; i < items.size(); ++i setFlag(ItemIsFocusable); setStyle(SPrompt::getNormalStyle()); items.size() != cons->size() errors.setStyle(SInvisible::getNormalStyle()); m->setPos( 0 , 350 ); CStatement* st = dynamic_cast (item); setZValue(5); contents.setStyle(SInvisible::getNormalStyle()); delete error1; removeAllItems(); max_height < items[i]->boundingRect().height() updateRepresentation(); errors.setStyle(SInvisible::getNormalStyle()); st error1 = NULL; module_name == "styles" && class_name == "SClass" && method_name == "getTitleStyle" int current_width = 0; max_height = items[i]->boundingRect().height(); errors.setOptions(true, false, 3); items.push_back((R*) VisFactory::getNewStatementVis(this, st)); items.push_back(new R(this, item)); delete error2; m->setPos( 0 , 700 ); int current_height = 0; contents.setOptions(true, false, 4); max_width < items[i]->boundingRect().width() error2 = NULL; int biggest_height = 0; i == 0 contents.addItem(&text); module_name == "styles" && class_name == "SClassName" && method_name == "getNormalStyle" max_width = items[i]->boundingRect().width(); size_t i = 0; i < cons->size(); ++i setFocusProxy(*items.begin()); contents.addItem(&errors); errors.size() == 0 && !prompt->getError1().getText().isEmpty() m->setPos( 0 , 0 ); setClient(&contents); R* rep = new R(this, (*cons)[i]); total_width += items[i]->boundingRect().width() + distance; focus error1 = new VText(&errors, &prompt->getError1()); module_name == "styles" && class_name == "SIfElse" && method_name == "getThenStyle" total_height += items[i]->boundingRect().height() + distance; setZValue(6); errors.addItem(error1); items.push_back( rep ); int max_height = 0; text.setFocus(); m->setPos( 0 , 450 ); rep->setPos(getPadding() + current_width, getPadding()); int max_width = 0; error2 = new VText(&errors, &prompt->getError2()); total_width -= distance; painter option widget text.visualizationChanged(); Position* p = dynamic_cast< Position* > ( (*cons)[i] ); paint errors.addItem(error2); int total_width = 0; total_height -= distance; QPainter* const QStyleOptionGraphicsItem* QWidget* module_name == "styles" && class_name == "SIfElse" && method_name == "getElseStyle" errors.setStyle(SPrompt::getErrorStyle()); p && p->isAutoPlaced() == false int total_height = 0; vertical painter->setFont(style->font); contents.updateRepresentation(); m->setPos( 0 , 800 ); rep->setPos(p->getX(), p->getY() ); rep->setPos(getPadding() + current_width, getPadding() + current_height); size_t i = 0; i < items.size(); ++i setClientSize(max_width, total_height); setClientSize(total_width, max_height); painter->setPen( style->pen ); module_name == "styles" && class_name == "SIfElse" && method_name == "getHeaderStyle" int width_to_add = p->getX() + rep->boundingRect().width() - current_width; current_width += rep->boundingRect().width(); VBox::updateRepresentation(); max_height < items[i]->boundingRect().height() int total = 0; painter->drawText(bounding_rect, Qt::TextExpandTabs, text->getText()); width_to_add >0 m->setPos( 0 , 1400 ); hasFocus() max_height = items[i]->boundingRect().height(); size_t i = 0; i < items.size(); ++i current_width += width_to_add; module_name == "styles" && class_name == "SInvisible" && method_name == "getNormalStyle" painter->setPen( QPen(Qt::black, 2) ); max_width < items[i]->boundingRect().width() int x_offset = 0; painter->drawLine(cursor_x, bounding_rect.top(), cursor_x, bounding_rect.bottom()); m->setPos( 1 , 25 ); rep->y() + rep->boundingRect().height() > biggest_height max_width = items[i]->boundingRect().width(); int y_offset = 0; vertical painter->setFont( QFont() ); module_name == "styles" && class_name == "SLoop" && method_name == "getErrorStyle" biggest_height = rep->y() + rep->boundingRect().height(); total_width += items[i]->boundingRect().width() + distance; updateRepresentation m->setPos( 0 , 400 ); i>0 && i%3 == 0 total_height += items[i]->boundingRect().height() + distance; justified justified current_height = biggest_height; total_width -= distance; x_offset = (max_width - items[i]->boundingRect().width()) / 2; y_offset = (max_height - items[i]->boundingRect().height()) / 2; QFont font("Arial", 12, style->font.weight(), style->font.italic() ); module_name == "styles" && class_name == "SLoop" && method_name == "getHeaderStyle" font.setPixelSize(style->font.pixelSize()); current_width = 0; total_height -= distance; items[i]->setPos(getPadding() + x_offset, getPadding() + total); items[i]->setPos(getPadding() + total, getPadding() + y_offset); m->setPos( 0 , 800 ); QString msg = text->getText(); vertical total += items[i]->boundingRect().height() + distance; total += items[i]->boundingRect().width() + distance; msg.length() == 0 module_name == "styles" && class_name == "SMethod" && method_name == "getNormalStyle" int min_x = 0; setClientSize(max_width, total_height); setClientSize(total_width, max_height); VBox::updateRepresentation(); m->setPos( 5 , 5 ); int max_x = 0; new_item removeAllItems msg = " "; int total = 0; addItem int min_y = 0; V* QFontMetrics fmet(font); module_name == "styles" && class_name == "SMethod" && method_name == "getErrorStyle" size_t i = 0; i < items.size(); ++i items.clear(); int max_y = 0; QSize s = fmet.size(Qt::TextExpandTabs, msg); m->setPos( 0 , 400 ); new_item items.size() > 0 vertical bounding_rect.setWidth(s.width() + EXTRA_TEXT_SPACE); items.push_back(new_item); module_name == "styles" && class_name == "SMethod" && method_name == "getGrayStyle" min_x = items[0]->pos().x(); items[i]->setPos(getPadding(), getPadding() + total); items[i]->setPos(getPadding() + total, getPadding()); bounding_rect.setHeight(s.height()); m->setPos( 550 , 0 ); max_x = items[0]->boundingRect().width() + min_x; total += items[i]->boundingRect().height() + distance; total += items[i]->boundingRect().width() + distance; updateRepresentation(); bounding_rect.moveTop(0); bounding_rect.moveLeft(0); min_y = items[0]->pos().y(); module_name == "styles" && class_name == "SMethod" && method_name == "getHighlightStyle" max_y = items[0]->boundingRect().height() + max_y; VBoxi:s:uCpodnastetrRuecptIrdeesnentittaytCiohna()n;ged removeAllItems hasFocus() m->setPos( 550 , 450 ); QString cursorleft = msg.left(cursor.pos); size_t i = 0; i < items.size(); ++i bool ok = items.size() == cons->size(); size_t i = 0; i < items.size(); ++i ok cursor_x = fmet.size(Qt::TextExpandTabs, cursorleft).width(); module_name == "styles" && class_name == "SMethod" && method_name == "getSourceStyle" delete items[i]; min_x > items[i]->pos().x() m->setPos( 550 , 900 ); int i = 0; i < items.size(); ++i items[i] = NULL; min_x = items[i]->pos().x(); module_name == "styles" && class_name == "SMethod" && method_name == "getDestinationStyle" ok = ok && items[i]->getConstruct() == (*cons)[i]; items.clear(); min_y > items[i]->pos().y() m->setPos( 550 , 1300 ); min_y = items[i]->pos().y(); return !ok; module_name == "styles" && class_name == "SMethod" && method_name == "getStackTraceStyle" max_x < items[i]->pos().x() + items[i]->boundingRect().width() m->setPos( 0 , 1200 ); max_x = items[i]->pos().x() + items[i]->boundingRect().width(); module_name == "styles" && class_name == "SMethod" && method_name == "getTitleStyle" max_y < items[i]->pos().y() + items[i]->boundingRect().height() m->setPos( 0 , 850 ); max_y = items[i]->pos().y() + items[i]->boundingRect().height(); module_name == "styles" && class_name == "SModule" && method_name == "getErrorStyle" setClientSize(max_x - getPadding(), max_y - getPadding()); m->setPos( 0 , 400 ); VBox::updateRepresentation(); module_name == "styles" && class_name == "SModuleName" && method_name == "getHugeStyle" m->setPos( 0 , 200 ); VStackTrace VRuntimeVariableInfo module_name == "styles" && class_name == "SPrompt" && method_name == "getNormalStyle" method_seq VLinear > dec CVariableDeclaration* m->setPos( 21 , -7 ); cons_name CText v VLinear VSpacer VBoxed vis_name VBoxed name VText module_name == "styles" && class_name == "SPrompt" && method_name == "getErrorStyle" methods std::list cinfo CText m->setPos( 1 , 249 ); item V spacers std::list vinfo VText parent last_vars VLinear* module_name == "styles" && class_name == "SStackTrace" && method_name == "getCallStatementStyle" VSpacer parent cons VBase* VBoxed parent dec getName m->setPos( 650 , 0 ); VBase* C* VRuntimeVariableInfo bounding_rect.setCoords(0,0,0,0); parent ~VStackTrace VBase* CVariableDeclaration* VStackTrace return name; module_name == "styles" && class_name == "SText" && method_name == "SText" setClient(&item); VBase* clearAll(); name.setZValue(v.zValue() + 1); m->setPos( 0 , 0 ); setStyle(SStackTrace::getNormalStyle()); vinfo.setStyle(SVarInfo::getNormalStyle()); getVInfo cons_name.setText("Stack Trace 1"); v.setOptions(false, false, 5); module_name == "styles" && class_name == "SText" && method_name == "~SText" return vinfo; method_seq.setOptions(false, false, 30); v.addItem(&name); QStyleOptionGraphicsItem* QWidget* m->setPos( 200 , 0 ); paint setClient(&method_seq); v.addItem(&vinfo); QPainter* const setHeader(&vis_name); setStyle(SInvisible::getNormalStyle()); getCInfo module_name == "styles" && class_name == "SVarInfo" && method_name == "getNormalStyle" setClient(&v); return cinfo; m->setPos( 0 , 0 ); updateRepresentation module_name == "vis" && class_name == "ClassIcon" && method_name == "paint" m->setPos( 0 , 300 ); width height module_name == "vis" && class_name == "ClassIcon" && method_name == "updateRepresentation" setSize int int m->setPos( 0 , 200 ); method height highlight_line begin count bounding_rect.setCoords(0,0,width,height); addMethod highlightStatement module_name == "vis" && class_name == "IfElseIcon" && method_name == "IfElseIcon" CMethod* int int VSequence& int m->setPos( 0 , 0 ); VLinear* col = new VLinear (&method_seq); int i = 0; i < begin.size(); ++i col->setOptions(true, false, 10); module_name == "vis" && class_name == "IfElseIcon" && method_name == "paint" VLoop* loop = dynamic_cast (begin[i]); VMethod* vm = new VMethod(col, method); m->setPos( 0 , 300 ); VSpacer* sp = new VSpacer(col); loop module_name == "vis" && class_name == "IfElseIcon" && method_name == "updateRepresentation" VLinear* vars = new VLinear (col); count = highlightStatement(loop->getBody(), count); vars->setOptions(true, false, 2); continue; m->setPos( 0 , 200 ); last_vars = vars; VIfElse* ifelse = dynamic_cast (begin[i]); module_name == "vis" && class_name == "LoopIcon" && method_name == "paint" highlight_line >= 0 ifelse m->setPos( 0 , 300 ); highlightStatement(vm->getStatements(), highlight_line); count = highlightStatement(ifelse->getThen(), count); module_name == "vis" && class_name == "LoopIcon" && method_name == "updateRepresentation" sp->setSize(10, height); count = highlightStatement(ifelse->getElse(), count); vm->setStyle(SMethod::getStackTraceStyle()); continue; m->setPos( 0 , 200 ); col->addItem(sp); count == 0 module_name == "vis" && class_name == "MethodIcon" && method_name == "MethodIcon" col->addItem(vars); m->setPos( 0 , 0 ); col->addItem(vm); begin[i]->setStyle(SStackTrace::getCallStatementStyle()); method_seq.addItem(col); --count; module_name == "vis" && class_name == "MethodIcon" && method_name == "paint" updateRepresentation(); return count; m->setPos( 0 , 350 ); var info addVarInfo module_name == "vis" && class_name == "MethodIcon" && method_name == "updateRepresentation" CVariableDeclaration* QString m->setPos( 0 , 250 ); last_vars module_name == "vis" && class_name == "VBase" && method_name == "updateIfConstructChanged" last_vars->setStyle(SMethod::getTitleStyle()); VRuntimeVariableInfo* vi = new VRuntimeVariableInfo(last_vars, var); m->setPos( 400 , 1450 ); vi->getCInfo().setText(info); module_name == "vis" && class_name == "VBase" && method_name == "updateAllChildren" last_vars->addItem(vi); m->setPos( 0 , 2100 ); clearAll module_name == "vis" && class_name == "VBase" && method_name == "updateRepresentationFrame" std::list::iterator it = methods.begin(); it != methods.end(); ++it m->setPos( 0 , 1550 ); delete (*it); module_name == "vis" && class_name == "VBase" && method_name == "setEventHandler" methods.clear(); m->setPos( 1550 , 200 ); last_vars = NULL; module_name == "vis" && class_name == "VBase" && method_name == "VBase" m->setPos( 27 , 8 );

module_name == "vis" && class_name == "VBase" && method_name == "~VBase" m->setPos( 450 , 0 );

module_name == "vis" && class_name == "VBase" && method_name == "updateModifiedItems" m->setPos( 0 , 1900 );

module_name == "vis" && class_name == "VBase" && method_name == "focus" m->setPos( 0 , 2450 );

module_name == "vis" && class_name == "VBase" && method_name == "focus" m->setPos( 0 , 2450 );

module_name == "vis" && class_name == "VBase" && method_name == "setConsWithPrompt" m->setPos( 0 , 1100 );

module_name == "vis" && class_name == "VBase" && method_name == "getCursor" m->setPos( 1283 , 24 );

module_name == "vis" && class_name == "VBase" && method_name == "getHandler" styles vis_helpers m->setPos( 1282 , 247 ); SBox SApplication SModule SClass SMethod SIfElse SLoop module_name == "vis" && class_name == "VBase" && method_name == "constructChanged" background QBrush normal static SApplication* 500 static const int GRID_SIZE = normal static SModule* 100 static const int GRID_SIZE = normal static SClass* 50 static const int GRID_SIZE = normal static SMethod* normal static SIfElse* normal static SLoop* border QPen error static SApplication* error static SModule* error static SClass* error static SMethod* then_branch static SIfElse* error static SLoop* m->setPos( 1250 , 1000 ); shadow QBrush title static SBox* gray static SMethod* _branch _branch header static SBox* VisFactory module_name == "vis" && class_name == "VBase" && method_name == "visualizationChanged" corner_radius int getNormalStyle getNormalStyle highlight static SMethod* header static SBox* m->setPos( 1250 , 1100 ); shadow_x int getNormalStyle source static SMethod* getNormalStyle !normal !normal parent statement shadow_y int destination static SMethod* getNormalStyle module_name == "vis" && class_name == "VBase" && method_name == "onChildChange" !normal !normal getNewStatementVis padding_top int normal = new SApplication(); normal = new SModule(); stack_trace static SMethod* VBase* CStatement* !normal m->setPos( 1250 , 1200 ); padding_left int normal->border.setWidth(6); normal->border.setWidth(4); normal = new SClass(); title static SBox* normal = new SLoop(); CLoop* cl = dynamic_cast (statement); normal->setCorners(20, false); normal->setCorners(6, true); normal->border.setWidth(3); normal = new SIfElse(); normal->border.setWidth(1); padding_bottom int cl module_name == "vis" && class_name == "VBase" && method_name == "updateRepresentation" padding_right int normal->setShadowSize(15); normal->setShadowSize(0); normal->setCorners(5, false); getNormalStyle getGrayStyle normal->border.setWidth(1); normal->border.setColor(QColor(0x80,0x80,0xFF)); return new VLoop(parent, cl); CIfElse* cie = dynamic_cast (statement); m->setPos( 0 , 1450 ); icon_distance int normal->setPadding(60); normal->setPadding(15); normal->setShadowSize(0); normal->border.setColor(Qt::gray); normal->setCorners(2, true); !normal !gray header_distance int normal->background.setColor( Qt::white ); normal->padding_top = 30; normal->setPadding(20); normal->setCorners(2, true); normal->setShadowSize(0); cie module_name == "vis" && class_name == "VBase" && method_name == "getParent" normal->background.setColor(Qt::lightGray); normal->background.setColor(QColor(0xFF,0xFF,0xFF,0xC0)); normal = new SMethod(); gray = new SMethod(); normal->setShadowSize(0); normal->setPadding(10); cut_corners bool return normal; return new VIfElse(parent, cie) ; return new VStatement(parent, statement); m->setPos( 1277 , 140 ); header_centered bool normal->setSnapToGrid(GRID_SIZE); normal->setSnapToGrid(GRID_SIZE); normal->border.setWidth(2); gray->border.setWidth(1); normal->setPadding(2); normal->setFadeBackground(QColor(Qt::white), QColor(0xD0, 0xD0, 0xFF)); normal->setCorners(15, false); gray->border.setColor(Qt::lightGray); normal->padding_left = 10; normal->header_centered = false; module_name == "vis" && class_name == "VBase" && method_name == "hasPrompt" snap_to_grid int return normal; return normal; normal->setShadowSize(0); gray->setCorners(15, false); normal->background.setColor(Qt::white); m->setPos( 0 , 1000 ); getErrorStyle return normal; SBox ~SBox normal->setPadding(5); gray->setShadowSize(0); normal->background.setColor(QColor(0xFF, 0xFF, 0xFF, 0x40)); getErrorStyle normal->header_centered = false; gray->setPadding(5); normal->header_centered = false; module_name == "vis" && class_name == "VBase" && method_name == "requestPrompt" !error getErrorStyle normal->background.setColor(Qt::white); gray->header_centered = false; m->setPos( 0 , 500 ); error = new SApplication(); !error return normal; getErrorStyle !error normal->setSnapToGrid(GRID_SIZE); gray->setFadeBackground(QColor(0xFF, 0xFF, 0xFF, 0x00), QColor(0xC0, 0xC0, 0xC0, 0x80)); top bottom error->border.setWidth(6); error = new SClass(); module_name == "vis" && class_name == "VBase" && method_name == "removePrompt" setFadeBackground error = new SModule(); gray->setSnapToGrid(GRID_SIZE); !error QColor QColor error->setCorners(20, false); error->border.setWidth(3); return normal; m->setPos( 650 , 500 ); error->setShadowSize(15); error->border.setWidth(4); error->setCorners(5, false); return gray; getThenStyle error = new SLoop(); QLinearGradient gradient(0, 0, 0, 200); error->setPadding(60); error->setCorners(6, true); error->setShadowSize(0); error->border.setWidth(1); module_name == "vis" && class_name == "VBase" && method_name == "showPrompt" gradient.setColorAt(0, top); !then_branch error->setFadeBackground(Qt::white, Qt::red); error->setShadowSize(0); error->setPadding(20); getErrorStyle error->border.setColor(Qt::red); m->setPos( 0 , 800 ); gradient.setColorAt(1, bottom); then_branch = new SIfElse(); error->setPadding(15); error->setFadeBackground(Qt::white, Qt::red); error->setCorners(2, true); return error; !error then_branch->border.setStyle(Qt::NoPen); module_name == "vis" && class_name == "VBase" && method_name == "showPrompt" background = QBrush(gradient); error->padding_top = 30; error->setSnapToGrid(GRID_SIZE); getHighlightStyle error->setShadowSize(0); error->setFadeBackground(Qt::white, Qt::red); error = new SMethod(); then_branch->setCorners(2, true); error->setPadding(10); m->setPos( 0 , 800 ); return error; !highlight error->setSnapToGrid(GRID_SIZE); error->border.setWidth(2); then_branch->setShadowSize(0); error->background.setColor(Qt::red); error->border.setColor(Qt::red); highlight = new SMethod(); then_branch->setPadding(2); error->header_centered = false; module_name == "vis" && class_name == "VBase" && method_name == "HasFocusInHierarchy" setPadding padding top left bottom right return error; setPadding getTitleStyle error->setCorners(15, false); highlight->border.setWidth(4); then_branch->background.setColor(QColor(0xD0, 0xFF, 0xD0, 0xA0)); m->setPos( 350 , 2450 ); int int int int int return error; error->setShadowSize(0); highlight->border.setColor(Qt::green); !title return then_branch; module_name == "vis" && class_name == "VBase" && method_name == "boundingRect" padding_top = padding; error->setPadding(5); highlight->setCorners(15, false); padding_left = padding; title = new SBox(); error->header_centered = false; highlight->setShadowSize(6); m->setPos( 1275 , 338 ); getHeaderStyle padding_bottom = padding; title->border.setWidth(2); error->background.setColor(Qt::red); highlight->setPadding(5); getElseStyle module_name == "vis" && class_name == "VBase" && method_name == "type" padding_right = padding; title->setCorners(15, false); error->setSnapToGrid(GRID_SIZE); highlight->header_centered = false; !header m->setPos( 1280 , 429 ); padding_top = top; title->setShadowSize(0); highlight->background.setColor( QColor(0x40, 0x40, 0xFF) ); !else_branch return error; header = new SBox(); padding_left = left; title->setPadding(5); highlight->setSnapToGrid(GRID_SIZE); else header->border.setWidth(1); module_name == "vis" && class_name == "VBase" && method_name == "mouseMoveEvent" padding_bottom = bottom; return title; return highlight; _branch = new SIfElse(); header->border.setColor(Qt::gray); m->setPos( 1250 , 1400 ); padding_right = right; else header->setCorners(5, false); getTitleStyle _branch->border.setStyle(Qt::NoPen); header->setShadowSize(0); module_name == "vis" && class_name == "VBase" && method_name == "mouseReleaseEvent" else header->setPadding(2); m->setPos( 1250 , 1550 ); setTransparentBackground !title getSourceStyle _branch->setCorners(2, true); header->background.setColor(Qt::white); module_name == "vis" && class_name == "VBase" && method_name == "keyPressEvent" background = QBrush(QColor(Qt::transparent)); title = new SBox(); else !source return header; title->border.setWidth(2); _branch->setShadowSize(0); m->setPos( 1250 , 1700 ); title->setCorners(7, true); source = new SMethod(); grid_size else module_name == "vis" && class_name == "VBox" && method_name == "roundedRect" setSnapToGrid source->border.setWidth(2); int title->setShadowSize(0); _branch->setPadding(2); source->setCorners(15, false); m->setPos( 0 , 1900 ); title->setPadding(5); else grid_size >= 0 source->setShadowSize(0); title->header_centered = false; _branch->background.setColor(QColor(0xFF, 0xD0, 0xD0, 0xA0)); module_name == "vis" && class_name == "VBox" && method_name == "setClientSize" source->setPadding(5); snap_to_grid = grid_size; return title; source->header_centered = false; return m->setPos( 2450 , 600 ); else source->background.setColor( QColor(0x40, 0xFF, 0x40) ); module_name == "vis" && class_name == "VBox" && method_name == "itemChange" source->setSnapToGrid(GRID_SIZE); _branch; radius cut m->setPos( 2050 , 1900 ); setCorners getStackTraceStyle int bool return source; !stack_trace module_name == "vis" && class_name == "VBox" && method_name == "getClientWidth" corner_radius = radius; m->setPos( 2000 , 0 ); stack_trace = new SMethod(); getHeaderStyle cut_corners = cut; getDestinationStyle stack_trace->border.setWidth(2); module_name == "vis" && class_name == "VBox" && method_name == "getClientHeight" stack_trace->setCorners(15, false); !header size !destination m->setPos( 2000 , 100 ); setShadowSize stack_trace->setShadowSize(0); header = new SBox(); int destination = new SMethod(); stack_trace->setPadding(5); header->border.setWidth(1); module_name == "vis" && class_name == "VBox" && method_name == "getPadding" destination->border.setWidth(2); shadow_x = size; stack_trace->header_centered = false; header->border.setColor(Qt::gray); destination->setCorners(15, false); m->setPos( 2000 , 200 ); shadow_y = size; stack_trace->background.setColor(Qt::white); header->setCorners(4, true); destination->setShadowSize(0); header->setShadowSize(0); module_name == "vis" && class_name == "VBox" && method_name == "VBox" return stack_trace; destination->setPadding(5); header->setPadding(2); m->setPos( 0 , 0 ); destination->header_centered = false; header->background.setColor(Qt::white); destination->background.setColor( QColor(0xFF, 0x40, 0x40) ); module_name == "vis" && class_name == "VBox" && method_name == "setStyle" destination->setSnapToGrid(GRID_SIZE); return header; m->setPos( 2450 , 450 ); return destination; module_name == "vis" && class_name == "VBox" && method_name == "getStyle" m->setPos( 2000 , 300 );

module_name == "vis" && class_name == "VBox" && method_name == "paint" m->setPos( 0 , 1550 );

module_name == "vis" && class_name == "VBox" && method_name == "setClient" m->setPos( 2450 , 0 );

module_name == "vis" && class_name == "VBox" && method_name == "setHeader" m->setPos( 2450 , 150 );

module_name == "vis" && class_name == "VBox" && method_name == "setIcon" m->setPos( 2450 , 300 ); SPrompt SInvisible module_name == "vis" && class_name == "VBox" && method_name == "updateRepresentation" normal static SPrompt* normal static SInvisible* m->setPos( 0 , 700 ); error static SPrompt* getNormalStyle module_name == "vis" && class_name == "VBoxed" && method_name == "VBoxed" getNormalStyle !normal m->setPos( 33 , 9 ); !normal normal = new SInvisible(); Envision module_name == "vis" && class_name == "VClass" && method_name == "VClass" Envision EnView normal = new SPrompt(); normal->border = QPen(Qt::NoPen); m->setPos( 0 , 0 ); normal->background.setColor(QColor(0xFF, 0xCC, 0x33)); normal->setShadowSize(0); ui Ui::EnvisionClass normal->setPadding(1); module_name == "vis" && class_name == "VClassField" && method_name == "VClassField" return normal; eveenvtent normal->header_distance = 0; 0 ~Envision EnView kceuysPtormesEsvEevnetnt m->setPos( 0 , 0 ); Envision QWidget* QEQvKeenytE *vent* getErrorStyle normal->setTransparentBackground(); QWidget *parent = QGraphicsScene *scene = new QGraphicsScene(this); module_name == "vis" && class_name == "VClassField" && method_name == "getName" return normal; event->ktyepye()( )= === Q Utp::dKaetye_EFv1e2n t|:|: Eevent-T>ykpeey() == Qt::Key_F11 !error ui.setupUi(this); HBase::setView(this); m->setPos( 800 , 100 ); QVBParisnete::ru prd(aQtePMrinotdeirfi:e:HdIigtehmRse(s)o; lutiQonG)r;aphicsView::customEvent(event); QGraphicsView::keyPressEvent(event); setCentralWidget(ui.main_view); scene->setItemIndexMethod(QGraphicsScene::NoIndex); error = new SPrompt(); pCro.snesttOruucttp::udteFlieleteNEarmasee("de(n);v.pdf"); module_name == "vis" && class_name == "VClassField" && method_name == "getType" setScene(scene); error->border.setColor(Qt::red); pr.setFullPage(true); Parser p; m->setPos( 800 , 0 ); error->background.setColor(QColor(0xFF, 0xA0, 0xA0)); QSvgGenerator svggen; CApplication* a = p.getApp("/home/mitko/workspace/Envision"); return error; svggen.setResolution(90); module_name == "vis" && class_name == "VDummy" && method_name == "VDummy" VApplication* box = new VApplication(NULL, a); event->key() == Qt::Key_F11 scene->addItem(box); m->setPos( 0 , 0 ); pr.setPaperSize(QSize(viewport()->rect().width(), viewport()->rect().height()), QPrinter::Point); pr.setPaperSize(QSize(scene()->sceneRect().width(), scene()->sceneRect().height()), QPrinter::Point); box->setPos(-(int) box->boundingRect().width() / 2, -(int) box->boundingRect().height() / 2); module_name == "vis" && class_name == "VDummy" && method_name == "updateRepresentation" setWindowTitle(tr("Envision")); QPainter pdfp(&pr); QPainter pdfp(&pr); SText SModuleName SClassName SMethodName SType SVarInfo SStackTrace Ui_EnvisionClass m->setPos( 0 , 200 ); setTransformationAnchor(AnchorUnderMouse); render(&pdfp); scene()->render(&pdfp); font QFont normal static SModuleName* normal static SClassName* normal static SMethodName* normal static SType* normal static SVarInfo* normal static SStackTrace* main_view EnView* svggen.setFileName("view.svg"); svggen.setFileName("scene.svg"); module_name == "vis" && class_name == "VDummy" && method_name == "get" pen QPen huge static SModuleName* call_statement static SBox* statusbar QStatusBar* svggen.setSize(QSize(viewport()->rect().width(), viewport()->rect().height())); svggen.setSize(QSize(scene()->sceneRect().width(), scene()->sceneRect().height())); m->setPos( 500 , 0 ); getNormalStyle getNormalStyle getNormalStyle getNormalStyle QPainter svgPainter(&svggen); QPainter svgPainter(&svggen); SText ~SText getNormalStyle getNormalStyle getCallStatementStyle EnvisionClass event !normal !normal !normal !normal setupUi wheelEvent render(&svgPainter); scene()->render(&svgPainter); module_name == "vis" && class_name == "VFields" && method_name == "clearAll" QMainWindow* QWheelEvent* font.setPixelSize(20); svgPainter.end(); svgPainter.end(); m->setPos( 0 , 350 ); !normal normal = new SClassName(); normal = new SMethodName(); normal = new SType(); normal = new SVarInfo(); !normal !call_statement EnvisionClass->objectName().isEmpty() static qreal factor = 1.0; normal = new SModuleName(); normal->font.setPixelSize(24); normal->font.setPixelSize(20); normal->font.setItalic(true); normal->font.setItalic(true); normal = new SStackTrace(); call_statement = new SBox(); module_name == "vis" && class_name == "VFields" && method_name == "VFields" normal->font.setBold(true); normal->font.setFamily("Times"); normal->font.setBold(true); normal->pen.setColor(Qt::blue); normal->pen.setColor(Qt::darkYellow); normal->border.setWidth(2); call_statement->border = QPen(Qt::NoPen); EnvisionClass->setObjectName(QString::fromUtf8("EnvisionClass")); event->delta() > 0 m->setPos( 0 , 0 ); normal->font.setItalic(true); normal->setCorners(15, false); call_statement->background.setColor(QColor(0xFF, 0xA0, 0x00)); factor *= 0.75; return normal; return normal; return normal; return normal; EnvisionClass->resize(600, 600); factor < 4.0 normal->font.setBold(true); normal->setShadowSize(10); call_statement->setShadowSize(0); scaleView(0.75); module_name == "vis" && class_name == "VFields" && method_name == "~VFields" main_view = new EnView(EnvisionClass); factor *= 1.5; normal->pen.setColor(Qt::blue); normal->setPadding(10); call_statement->setPadding(1); getHugeStyle main_view->setObjectName(QString::fromUtf8("main_view")); scaleView(1.5); m->setPos( 550 , 0 ); normal->header_centered = true; return normal; return call_statement; main_view->setFocusPolicy(Qt::StrongFocus); !huge normal->setFadeBackground(Qt::white, QColor(0xFF, 0xA0, 0xA0) ); module_name == "vis" && class_name == "VFields" && method_name == "updateRepresentation" main_view->setFrameShape(QFrame::NoFrame); huge = new SModuleName(); return normal; main_view->setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing); m->setPos( 0 , 650 ); scaleFactor huge->font.setPointSize(400); main_view->setCacheMode(QGraphicsView::CacheBackground); scaleView qreal module_name == "vis" && class_name == "VFields" && method_name == "paint" return huge; main_view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); m->setPos( 0 , 1300 ); main_view->setResizeAnchor(QGraphicsView::AnchorUnderMouse); scale(scaleFactor, scaleFactor); main_view->setOptimizationFlags(QGraphicsView::DontSavePainterState); module_name == "vis" && class_name == "VGrid" && method_name == "removeAllItems" EnvisionClass->setCentralWidget(main_view); m->setPos( 0 , 150 ); statusbar = new QStatusBar(EnvisionClass); statusbar->setObjectName(QString::fromUtf8("statusbar")); EnvisionClass module_name == "vis" && class_name == "VGrid" && method_name == "VGrid" EnvisionClass->setStatusBar(statusbar); m->setPos( 23 , -4 ); retranslateUi(EnvisionClass); QMetaObject::connectSlotsByName(EnvisionClass); module_name == "vis" && class_name == "VGrid" && method_name == "~VGrid" m->setPos( 250 , 0 );

module_name == "vis" && class_name == "VGrid" && method_name == "setXY" EnvisionClass retranslateUi QMainWindow* m->setPos( 2350 , 0 );

EnvisionClass->setWindowTitle(QApplication::translate("EnvisionClass", "EnvisionWindow", 0, QApplication::UnicodeUTF8)); module_name == "vis" && class_name == "VGrid" && method_name == "getX" m->setPos( 2150 , 100 );

module_name == "vis" && class_name == "VGrid" && method_name == "getY" m->setPos( 2150 , 0 );

module_name == "vis" && class_name == "VGrid" && method_name == "setItem" m->setPos( 2350 , 550 );

module_name == "vis" && class_name == "VGrid" && method_name == "removeItem" m->setPos( 400 , 150 );

module_name == "vis" && class_name == "VGrid" && method_name == "updateRepresentation" m->setPos( 0 , 500 );

module_name == "vis" && class_name == "VIfElse" && method_name == "switchFocus" m->setPos( 0 , 650 );

module_name == "vis" && class_name == "VIfElse" && method_name == "getThen" m->setPos( 1400 , 0 );

module_name == "vis" && class_name == "VIfElse" && method_name == "getElse" m->setPos( 1400 , 100 );

module_name == "vis" && class_name == "VLinear" && method_name == "removeAllItems" m->setPos( 300 , 1300 );

module_name == "vis" && class_name == "VLinear" && method_name == "setOptions" m->setPos( 1400 , 0 );

module_name == "vis" && class_name == "VLinear" && method_name == "addItem" m->setPos( 0 , 1300 );

module_name == "vis" && class_name == "VLinear" && method_name == "updateRepresentation" m->setPos( 0 , 400 );

module_name == "vis" && class_name == "VLoop" && method_name == "paint" m->setPos( 0 , 400 );

module_name == "vis" && class_name == "VMethod" && method_name == "VMethod" m->setPos( -7 , -1 );

module_name == "vis" && class_name == "VMethod" && method_name == "getStatements" m->setPos( 550 , 0 );

module_name == "vis" && class_name == "VMethodArgument" && method_name == "VMethodArgument" m->setPos( 0 , 0 );

module_name == "vis" && class_name == "VMethodArgument" && method_name == "VMethodArgument" m->setPos( 0 , 0 );

module_name == "vis" && class_name == "VMethodArgument" && method_name == "getName" m->setPos( 800 , 0 );

module_name == "vis" && class_name == "VMethodArgument" && method_name == "getType" m->setPos( 800 , 100 );

module_name == "vis" && class_name == "VModule" && method_name == "VModule" m->setPos( 0 , 0 );

module_name == "vis" && class_name == "VModule" && method_name == "updateRepresentation" m->setPos( 0 , 400 );

module_name == "vis" && class_name == "VModule" && method_name == "switchBigNames" m->setPos( 0 , 300 );

module_name == "vis" && class_name == "VPrompt" && method_name == "focus" m->setPos( 0 , 300 );

module_name == "vis" && class_name == "VRuntimeVariableInfo" && method_name == "VRuntimeVariableInfo" m->setPos( 0 , 0 );

module_name == "vis" && class_name == "VRuntimeVariableInfo" && method_name == "getName" m->setPos( 800 , 0 );

module_name == "vis" && class_name == "VRuntimeVariableInfo" && method_name == "getVInfo" m->setPos( 800 , 100 );

module_name == "vis" && class_name == "VRuntimeVariableInfo" && method_name == "getCInfo" m->setPos( 800 , 200 );

module_name == "vis" && class_name == "VSequence" && method_name == "removeAllItems" m->setPos( 500 , 1500 );

module_name == "vis" && class_name == "VSequence" && method_name == "isConstructIdentityChanged" m->setPos( 0 , 1500 );

module_name == "vis" && class_name == "VSequence" && method_name == "VSequence" m->setPos( 0 , 0 );

module_name == "vis" && class_name == "VSequence" && method_name == "~VSequence" m->setPos( 500 , 0 );

module_name == "vis" && class_name == "VSequence" && method_name == "getItems" m->setPos( 1100 , 0 );

module_name == "vis" && class_name == "VSequence" && method_name == "size" m->setPos( 1100 , 100 );

module_name == "vis" && class_name == "VSequence" && method_name == "operator[]" m->setPos( 1100 , 200 );

module_name == "vis" && class_name == "VSequence" && method_name == "setSequenceOptions" m->setPos( 1350 , 0 );

module_name == "vis" && class_name == "VSequence" && method_name == "updateRepresentation" m->setPos( 0 , 400 );

module_name == "vis" && class_name == "VSet" && method_name == "removeAllItems" m->setPos( 0 , 350 );

module_name == "vis" && class_name == "VSet" && method_name == "VSet" m->setPos( 0 , 0 );

module_name == "vis" && class_name == "VSet" && method_name == "~VSet" m->setPos( 400 , 0 );

module_name == "vis" && class_name == "VSet" && method_name == "updateRepresentation" m->setPos( 0 , 550 );

module_name == "vis" && class_name == "VSpacer" && method_name == "updateRepresentation" m->setPos( 0 , 300 );

module_name == "vis" && class_name == "VSpacer" && method_name == "paint" m->setPos( 0 , 200 );

module_name == "vis" && class_name == "VSpacer" && method_name == "setSize" m->setPos( 0 , 400 );

module_name == "vis" && class_name == "VStackTrace" && method_name == "clearAll" m->setPos( 0 , 1200 );

module_name == "vis" && class_name == "VStackTrace" && method_name == "highlightStatement" m->setPos( 550 , 400 );

module_name == "vis" && class_name == "VStackTrace" && method_name == "VStackTrace" m->setPos( 0 , 0 );

module_name == "vis" && class_name == "VStackTrace" && method_name == "~VStackTrace" m->setPos( 400 , 0 );

module_name == "vis" && class_name == "VStackTrace" && method_name == "addMethod" m->setPos( 0 , 400 );

module_name == "vis" && class_name == "VStackTrace" && method_name == "addVarInfo" m->setPos( 0 , 950 );

module_name == "vis" && class_name == "VStatement" && method_name == "getText" m->setPos( 550 , 0 );

module_name == "vis" && class_name == "VText" && method_name == "VText" m->setPos( 7 , 13 );

module_name == "vis" && class_name == "VText" && method_name == "paint" m->setPos( 0 , 400 );

module_name == "vis" && class_name == "VText" && method_name == "updateRepresentation" m->setPos( 0 , 700 );

module_name == "vis" && class_name == "VText" && method_name == "setStyle" m->setPos( 900 , 0 );

module_name == "vis_helpers" && class_name == "VisFactory" && method_name == "getNewStatementVis" m->setPos( 0 , 0 ); return m; Figure 12: A mock-up stack-trace from the execution of Envision. A vector graphic could not be produced. ware. Therefore all the comparisons in this section are based on preliminary results and projected features. This introspective evaluation is only an initial step. Proper quantitative studies are required to objectively asses the performance of our approach.

4.1 Implementation of the visualization principles

First we reflect on how the current concept of Envision implements the design principles outlined in section 3.2.

4.1.1 General-purpose

Envision is similar to a wrapper around an OOP language like C++, Java or C#. It retains the same object model and relationship between entities. It also inherits the general-purpose nature of those languages.

4.1.2 Efficient

We believe the proposed input methods which are a core part of Envision have the potential to be even more efficient that textual programming. This is evidently the case with the simple Hello World example presented earlier. To measure the efficiency of our approach on larger real-world programs, it would be first necessary to implement more features.

4.1.3 Fast

The current mock-up application is performing snappily even at more demanding tasks such as rendering of the entire Envision code base. Moreover this is done entirely in software using a single thread on a mid-range laptop. Therefore at its current stage of development Envision

20 meets the performance goal. To accommodate for future expansions we plan to employ rendering on dedicated graphics hardware.

4.1.4 Intuitive

Our system is based on OOP. So far the main visualization techniques are encapsulating entities into boxes and showing a tree hierarchy of the different entities. The former is natural when it comes to objects and data structures - enclosing them with a frame makes them more concrete and tangible. The latter is inherent in the structure of software written in the OOP paradigm. We think this contributes to the intuitiveness of Envision.

4.1.5 Flexible

We have shown that Envision is capable of covering the full spectrum of abstractions for a software application. It can show the entire application at once, outlining the relationship between different modules. Using the zoom feature it is possible to focus on a single module, class or method. At the method level one can directly observe the execution instructions of the program. This should provide enough flexibility for practically any task.

4.1.6 Focused

Envision’s interface is minimal, there are no menus, toolbars or multiple visible tools. Only source code fragments are shown and arranged in the way the programmer specifies. Common tasks can be performed directly using the prompt. We find this more efficient both in terms of speed and utilizing screen real estate.

4.1.7 Appropriate

We do not limit our approach to pure pictorial visualizations. The integration of text and visuals in Envision aims to match a concept with a natural representation. Our system is flexible and can provide for many different types of visualizations. A more objective evaluation and long-term experimentation is needed to determine which visualization styles are suited for what software fragments.

4.1.8 Self-sufficient

The current mock-up application just showcases some interesting aspects of the Envision concept. Much more work is needed before the application can be used to design programs. This is discussed further in section 5.

4.1.9 Varied

Presently Envision is rendered entirely using zoom-able vector graphics in full 32-bit color. Various effects such as anti-aliasing, font smoothing and translucency are also used. Our visualization styles include different shapes, colors and borders and as representation entities we use boxes, gradients, symbols, icons and text. Semantics are described by both the look of an object as well as by its position. As one can see we are already utilizing a wide variety of visual objects and plan to expand this further.

21 4.1.10 Friendly

Our experience with Envision’s current interface is positive. However there are many planned features that we need to implement before the application is functional. Since friendliness is a very subjective quality, only a large-scale experiment with programmers can help make conclusions.

4.2 Comparison to traditional programming environments

Here we evaluate how Envision’s approach compares to one of the most popular IDEs - Eclipse. Eclipse is a mature software development platform with a complete set of features and numerous extensions contributed by third parties. It is actively supported by both industrial organizations and by the open source community. Therefore a direct comparison between Envision and Eclipse at this time is unrealistic. Rather we will limit ourselves to the code editing and navigating functions. Furthermore we will only consider the standard Eclipse installation that does not include any third-party plug-ins.

4.2.1 Productivity

Productivity here refers to the speed at which one can write code. In Eclipse one has to type the entire structure of their program manually. A few exceptions are the use of the code completion feature and code wizards for e.g. new classes. In Envision the programmer creates structure by issuing commands. These commands are context sensitive and have a light syntax. In essence what the programmer types is not directly the software structure but is first interpreted by the environment based on the context to determine what programming fragment should be created. This concept is similar to sloppy programming [24, 17, 25]. This separation can be very powerful. For example when typing, the user can use a compact syntax similar to scripting or functional languages, while the result can be a standard OOP structure for languages like Java. We plan to enhance the command typing mechanism to aid the programmer in a way similar to code completion. This will further make it easier to quickly create complicated program structures. We feel that Envision will enable higher productivity compared to Eclipse as the input mechanisms we propose become more mature.

4.2.2 Readability

Eclipse provides a number of readability improvements over plain text file editors. These include syntax coloring, code elision and automatic formatting. The underlying representation however, remains purely textual. Envision on the other hand shows the entire program structure in one consistent tree hierarchy. It produces images which have a minimal number of distracting features. Common semantics are represented visually rather than via syntax and keywords. Only the core logic of the application appears in textual form. There is no need for formatting since code structure is automatically and explicitly visualized by the system. As a result, from a cognitive point of view, we believe our approach will improve readability. Graphically expressed features will be processed by the visual cognitive system. This is a very quick parallel process that is largely unconscious. Thus more capacity is left for deeper cognitive processes which are needed to process application-specific logic expressed as text.

22 4.2.3 Navigation

In Eclipse one navigates between different code fragments by switching to different tabs. Optionally one can search for a specific keyword, entity definition or object reference. It is also possible to click on an identifier in the editor window and navigate to its definition or uses. These mechanisms are extremely useful when the programmer is looking for specific information. Therefore we are planning to include such features. Additionally Envision allows the programmer to navigate code by looking at the software structure. Zooming out, one can see how different methods, classes and modules relate to each other. This is very useful when exploring the software for the first time. We believe that developers can use this feature to get more familiar with an unknown application faster.

4.2.4 Maintainability (Viscosity)

Maintainability here refers directly to one of the more severe problems identified by Green and Petre in [19] - resistance to local change. In their original analysis they concluded that visual programming languages reduce the user’s performance when local modifications are necessary to existing programs. They found that such tasks are done several times faster in traditional textual IDEs. On this point a textual IDE such as Eclipse facilitates high performance. Envision aims to achieve similar results. All textual fields visible on the screen can be directly modified. Since core logic is expressed as textual instructions they should allow for equally fast changes. One problem we experienced with the mock-up application is that each time there was a change to a program fragment we needed to manually adjust the positioning of surrounding entities. This is currently a hindrance in achieving low viscosity and we plan to include automatic positioning features to alleviate this problem.

4.2.5 Teaching

While Eclipse is an extremely powerful platform for software development, it has little to offer to novice programmers. We have experience in courses where non-programmers were learning to program in Java using Eclipse. Mistakes often made by beginners include misplacing of semicolons, confusing the scope of an identifier, using the wrong kind of parenthesis or brace and bad code formatting. Since Envision takes care of structure internally all of the above problems disappear. For small classroom examples Envision can be especially useful since the entire application can be visualized on a single screen.

5 Future work

Envision is still a work in progress. Many core concepts are planned for future development and the ones discussed here still need additional improvements. Here is a summary of possible future work:

• Quantitative analysis - Although this study serves to give an initial impression of the Envision system, it needs to be objectively validated. More information is needed on how expert programmers will interact with the system. Quantitative studies with developers must be carried out.

23 • Feature-full prototype - A feature-full prototype can be designed to enable better system evaluation and experimentation. Such a prototype would also be needed to support studies with programmers. • Concept refinement - The visualization and interaction concepts need further improve- ment. In particular the input system requires a more complete implementation that will provide more insight about its strengths and weaknesses. • Error reporting - we have barely scratched the surface of how Envision can benefit error reporting and debugging. More knowledge is needed about how our visual approach can improve existing practices.

• Information system implementation - We have not yet investigated the information system side of Envision. This track of research and development also presents many chal- lenges: How will semantic versioning work? What content can be linked and how? Which analysis techniques can we combine to improve the programming experience? How do we present the results of this analysis?

6 Conclusion

In this study we have presented Envision - a combination of an IDE for visual programming and an information system. Our contributions include developing the concept of Envision and its design principles, implementing a mock-up application to demonstrate its core ideas and comparing its performance to a modern text-based IDE - Eclipse. The evaluation of our approach carried out during this feasibility study is largely positive with some inconclusive results. There is a lot of future work needed to further develop the concept of Envision and validate its applicability. We strive to promote visual programming in large scale software development projects. The ultimate goal of Envision is to allow the production of higher-quality software faster.

References

[1] R.N. Shepard. Recognition memory for words, sentences, and pictures1. Journal of Verbal Learning and Verbal Behavior, 6(1):156–163, 1967. [2] L. Standing. Learning 10000 pictures. The Quarterly Journal of Experimental Psychology, 25(2):207–222, 1973. [3] S. Schiffer and J.H. Fr¨ohlich. Visual programming and software engineering with Vista. Visual object-oriented programming: concepts and environments, pages 199–227, 1995. [4] B.W. Chang, D. Ungar, and R.B. Smith. Getting close to objects: Object-focused program- ming environments. Visual object-oriented programming: concepts and environments, pages 185–198, 1995. [5] W. Citrin, M. Doherty, and B. Zorn. The design of a completely visual object-oriented programming language. Visual Object-Oriented Programming: Concepts and Environments. Prentice-Hall, New York, 1995.

[6] PT Cox, FR Giles, and T. Pietrzykowski. Prograph. Visual Object-Oriented Programming: Concepts and Environments. Prentice-Hall, New York, 1995. [7] C. Grant. The Visula programming language and environment. In IEEE Symposium on Visual Languages and Human-Centric Computing, 2006. VL/HCC 2006, pages 203–206, 2006.

24 [8] E. Baroth and C. Hartsough. Visual programming in the real world. In Visual object-oriented programming, page 42. Manning Publications Co., 1995. [9] R. DeLine, A. Khella, M. Czerwinski, and G. Robertson. Towards understanding programs through wear-based filtering. In Proceedings of the 2005 ACM symposium on Software visu- alization, pages 183–192. ACM, 2005.

[10] R. DeLine, M. Czerwinski, and G. Robertson. Easing program comprehension by sharing navigation data. 2005. [11] M. Cherubini, G. Venolia, R. DeLine, and A.J. Ko. Let’s go to the whiteboard: how and why software developers use drawings. In Proceedings of the SIGCHI conference on Human factors in computing systems, page 566. ACM, 2007. [12] R. DeLine. Staying oriented with software terrain maps. In International Conference on Distributed Multimedia Systems. Citeseer. [13] R. DeLine, M. Czerwinski, B. Meyers, G. Venolia, S. Drucker, and G. Robertson. Code thumbnails: Using spatial memory to navigate source code. 2006.

[14] M.A. Storey, C. Best, J. Michaud, D. Rayside, M. Litoiu, and M. Musen. SHriMP views: an interactive environment for information visualization and navigation. In CHI’02 extended abstracts on Human factors in computing systems, page 521. ACM, 2002. [15] A. Bragdon, R. Zeleznik, S.P. Reiss, S. Karumuri, W. Cheung, J. Kaplan, C. Coleman, F. Adeputra, and J.J. LaViola Jr. Code bubbles: a working set-based interface for code un- derstanding and maintenance. In Proceedings of the 28th international conference on Human factors in computing systems, pages 2503–2512. ACM, 2010. [16] A. Bragdon, S.P. Reiss, R. Zeleznik, S. Karumuri, W. Cheung, J. Kaplan, C. Coleman, F. Adeputra, and J.J. LaViola Jr. Code Bubbles: Rethinking the user interface paradigm of integrated development environments. In Proceedings of the 32nd ACM/IEEE International Conference on Software Engineering-Volume 1, pages 455–464. ACM, 2010. [17] G. Little and R.C. Miller. Keyword programming in java. Automated Software Engineering, 16(1):37–71, 2009. [18] K.N. Whitley. Visual programming languages and the empirical evidence for and against. Journal of Visual Languages and Computing, 8(1):109–142, 1997. [19] T.R.G. Green and M. Petre. Usability Analysis of Visual Programming Environments: A ’Cognitive Dimensions’ Framework. Journal of Visual Languages and Computing, 7(2):131– 174, 1996.

[20] M. Petre. Mental imagery and software visualization in high-performance software develop- ment teams. Journal of Visual Languages & Computing, 2010. [21] K.N. Whitley and A.F. Blackwell. Visual programming in the wild: A survey of LabVIEW programmers. Journal of Visual Languages & Computing, 12(4):435–472, 2001. [22] A. Cimino, F. Longo, and G. Mirabelli. A General Simulation Framework for Supply Chain Modeling: State of the Art and Case Study. Arxiv preprint arXiv:1004.3271, 2010. [23] S. Bangsow. Manufacturing Simulation with Plant Simulation and Simtalk: Usage and Pro- gramming with Examples and Solutions. Springer Verlag, 2010. [24] G. Little and R.C. Miller. Translating keyword commands into executable code. In Proceedings of the 19th annual ACM symposium on User interface software and technology, page 144. ACM, 2006.

25 [25] G. Little, T.A. Lau, A. Cypher, J. Lin, E.M. Haber, and E. Kandogan. Koala: capture, share, automate, personalize business processes on the web. In Proceedings of the SIGCHI conference on Human factors in computing systems, page 946. ACM, 2007.

26