Skip to main content

A gentle introduction to SWT and JFace, Part 3: How to use TabFolder, Canvas, and StyledText

And create stack layouts

Barry Feigenbaum, Programming Adviser, IBM, Software Group
Barry Feigenbaum, Ph.D., is a member of the IBM Worldwide Accessibility Center, which helps IBM make its products accessible to people with disabilities. He has published several books and articles, holds several patents, and has spoken at industry conferences, such as JavaOne. He has served as an adjunct assistant professor of computer science at the University of Texas at Austin and other schools.

Summary:  This installment of "A gentle introduction to SWT and JFace" expands on what you've learned about creating applications using Java™ technology, Eclipse, and the SWT and JFace libraries. Find out how to use tabular tree, canvas, styled text, slider, spinner, scale and other controls, as well as stack layouts.

View more content in this series

Date:  27 Sep 2005
Level:  Introductory
Activity:  1737 views

About this series

"A gentle introduction to SWT and JFace" includes basic articles on developing applications using the Standard Widget Toolkit (SWT) and JFace libraries that come with the base Eclipse software development kit. This series concentrates on using SWT and JFace for stand-alone applications. However, most of what you learn will apply to using them inside the Eclipse workbench.

We will start out with simple GUIs that have limited function and progress toward useful applications. We will introduce most of the standard and custom SWT widgets and many JFace features. Our discussion will include at least one example use of the technology.

This series assumes that you are familiar with the Java programming language and Java technology-based development, and have some understanding of the Java AWT or Swing GUI tool kits.

In the previous two installments of A gentle introduction to SWT and JFace, I introduced you to Eclipse, the Eclipse Standard Widget Toolkit (SWT) and the JFace GUI tool kits used to construct Eclipse and stand-alone rich GUIs. I also introduced the several basic GUI controls and container types. Then I showed how to combine these controls into simple working applications. I detailed how to provide those applications with a menu system. Finally, I demonstrated how you can follow best practice by creating a library of methods and classes to ease GUI development.

Here, I continue my survey of the various widgets in the org.eclipse.swt.custom and org.eclipse.swt.widgets packages. Unless otherwise noted, the controls I discuss are in the widgets package.

TableTree

In the previous installment, "How to use combo, list, table, and tree controls," I described the Tree and Table controls. SWT supports a hybrid version of these controls, in the custom package, called TableTree. In Eclipse V3.1, the Tree control was enhanced to be a functional replacement for TableTree, and TableTree was deprecated. Figure 1 shows an example Tree in tabular format (TableTree emulation mode). As you can see, each item in the tree is divided into columns. As the creation of both tables and trees were shown previously, and creating a tabular Tree is basically a combination of these two tasks, I will not repeat them here. The Tree sample included with this article demonstrates the creation of a tabular Tree. The code for creating a TableTree is very similar to the code I include for Tree, so if you need support for older versions of Eclipse, you can use the TableTree control.


Figure 1. Tabular Tree Example
Tabular Tree showing four members of a family and another person

The rest of this article will demonstrate the use of many new SWT controls. I will do this in the context of a single application called TabFolder1App.


TabFolder (and CTabFolder)

TabFolders are a convenient way to create complex GUIs that use limited amount of space. A tab folder is divided into one or more tabs, where each tab is a complete GUI of its own. Only one tab at a time is showing. A CTabFolder, in the custom package, is an enhanced version of a TabFolder that looks better and can support closure of tabs.

TabFolders and CTabFolders must be defined as one of two mutually exclusive styles:

  1. TOP -- Places tabs on the top.
  2. BOTTOM -- Places tabs on the bottom.

CTabFolder supports some additional optional styles:

  • FLAT -- Gives the folder a flat look.
  • BORDER -- Shows a border around the control.
  • CLOSE -- Allows the tab to close (shows a Close button).

Similar to Trees and Tables that contain items, TabFolders contain TabItems (or CTabItems) that define the tabs. TabFolders also contain multiple controls (typically, Composites), each one defining the contents of a tab. The TabItem.setControl method links the control to the associated tab.

Figure 2 shows a sample TabFolder, while Figure 3 shows a similar GUI using CTabFolder. Notice that the selected Canvas tab has a Close (X) button on the CTabFolder.


Figure 2. TabFolder with four tabs
Tab Folder showing four tabs with a drawing in the selected tab

Figure 3. CTabFolder with four tabs
Custom Tab Folder showing four tabs with a drawing in the selected tab

In keeping with the approach introduced in "How to use combo, list, table, and tree controls," I will use protected service methods (located in the super class BasicApplication) for creating controls. Listing 1 details the first of these methods and shows how TabFolders are created; similar code exists for CTabFolders.


Listing 1. Methods to create a TabFolder and TabItem

protected TabFolder createTabFolder(Composite parent, int style) {
    return new TabFolder(parent, style);
}
protected TabItem createTabItem(TabFolder parent, int style, 
                      String text, Image icon, Control ctl) {
    TabItem ti = new TabItem(parent, style);
    if (text != null) {
        ti.setText(text);
    }
    if (icon != null) {
        ti.setImage(icon);
    }
    if (ctl != null) {
        ti.setControl(ctl);
    }
    return ti;
}
protected TabItem createTabItem(TabFolder parent, 
                      String text, Image icon, Control ctl) {
    return createTabItem(parent, SWT.NONE, text, icon, ctl);
}


Canvas

One of the most basic control types is Canvas, which is used to either create custom controls or to draw pictures. Figure 2 and Figure 3 show examples of using a Canvas to draw a picture composed of overlapping rectangles and ovals. In this drawing, some figures are filled, and others are not. Colors, sizes, and positions are assigned randomly.

Listing 2 shows the code to create a Canvas. To actually draw on a Canvas, you must add a PaintListener to the Canvas. Its paintControl method is called whenever the Canvas needs to redraw any part of its client area. There are two styles of painting:

  1. Draw directly -- Simple, but the content is not stable across redraws.
  2. Build a model before any painting, then redraw from that model -- More complex, but stable. This is generally the preferred method.

Listing 2. Methods to create a Canvas

protected Canvas createCanvas(Composite parent, int style, 
                              PaintListener pl) {
    Canvas c = new Canvas(parent, style);
    if (pl != null) {
        c.addPaintListener(pl);
    }
    return c;
}
protected Canvas createCanvas(Composite parent, PaintListener pl) {
    return createCanvas(parent, SWT.NONE, pl);
}

As an example of painting style 2, consider the simple model defined in Listing 3:


Listing 3. Hierarchy of PaintItems

abstract protected class PaintItem {
    public Color color;
    public void paint(GC gc) {
        gc.setForeground(color);
        gc.setBackground(color);
    }
}
abstract protected class BaseRectItem extends PaintItem {
    public boolean fill;
    public Rectangle extent;
}
protected class ElipseItem extends BaseRectItem {
    public void paint(GC gc) {
        super.paint(gc);
        if (fill) {
            gc.fillOval(extent.x, extent.y, 
                        extent.width, extent.height);
        }
        else {
            gc.drawOval(extent.x, extent.y, 
                        extent.width, extent.height);
        }
    }
}
protected class RectangleItem extends BaseRectItem {
    public void paint(GC gc) {
        super.paint(gc);
        if (fill) {
            gc.fillRectangle(extent.x, extent.y, 
                             extent.width, extent.height);
        }
        else {
            gc.drawRectangle(extent.x, extent.y, 
                             extent.width, extent.height);
        }
    }
}

These paint items are drawn by the PaintListener shown in Listing 4. The paintControl method is provided with a graphic context (GC in the org.eclipse.swt.graphics package) on which to draw. You may draw text and many shapes using the GC. This code reuses standard system colors available through the Display class. It is up to the Canvas to fill its extent with a background color. The gcObjects collection includes all the PaintItem instances that need to be drawn. Array colorIds is a mapping to selected system colors.


Listing 4. Methods to create a TabFolder and TabItem

… new PaintListener() {
    public void paintControl(PaintEvent e) {
        GC gc = e.gc;
        gc.setBackground(canvas.getDisplay().
             getSystemColor(colorIds[0]));
        Point cext = canvas.getSize();
        gc.fillRectangle(0, 0, cext.x, cext.y);
        for (Iterator i = gcObjects.iterator(); 
             i.hasNext();) {
             PaintItem pi = (PaintItem)i.next();
             pi.paint(gc);
        }
   }
}…

protected static int[] colorIds = {
    SWT.COLOR_WHITE, SWT.COLOR_BLUE, SWT.COLOR_CYAN, 
    SWT.COLOR_GRAY, SWT.COLOR_GREEN, SWT.COLOR_MAGENTA, 
    SWT.COLOR_RED, SWT.COLOR_YELLOW, SWT.COLOR_BLACK
};

The code to clear the drawing, then create the drawing as a set of rectangles and ellipses is shown in Listing 5. This code is activated by buttons on the GUI.


Listing 5. Methods to process drawing events

public void doClear() {
    gcObjects.clear();
    canvas.redraw();
}

public void doDraw() {
    gcObjects.clear();
    Display display = drawButton.getDisplay();
    // create a bunch of objects
    for (int i = 0; i < 50; i++) {
        if (i % 2 == 0) {
            RectangleItem ri = new RectangleItem();
            ri.extent = new Rectangle(nextInt(500), nextInt(250), 
                                      nextInt(500), nextInt(250));
            ri.color = display.
                getSystemColor(colorIds[nextInt(colorIds.length)]);
            ri.fill = i % 3 == 0;
            gcObjects.add(ri);
        }
        else {
            ElipseItem ei = new ElipseItem();
            ei.extent = new Rectangle(nextInt(500), nextInt(250), 
                                      nextInt(500), nextInt(250));
            ei.color = display.
                getSystemColor(colorIds[nextInt(colorIds.length)]);
            ei.fill = i % 5 == 0;
            gcObjects.add(ei);
        }
    }
    canvas.redraw();
}


Spinner, Slider, Scale, and ProgressBar

SWT supports several ways to enter discrete numeric values. A Scale allows you to pick a value in a (typically small) integer range. A Slider allows you to pick a value in a (possibly large) integer range using a scroll bar-like approach. A Spinner allows you to pick (via advance/retreat buttons) or type in a (possibly fractional) number. Note that the Spinner is new in Eclipse V3.1. A ProgressBar is like an output-only Slider in that it can be used to show incremental activity (progress).

In general, these controls allow you to supply a minimum, maximum, and initial value. Except for ProgressBars, they also support increment and page increment values and, for Sliders, thumb width. Figure 4 shows a GUI with a Slider, a Spinner, and a Scale within the group and a ProgressBar below them. Immediately above the progress bar is a (centered) Label that shows the value of the progress bar.


Figure 4. Control examples
Custom Tab Folder showing Progress Bar tab containing Slider, Spinner, Scale, ProgressBar, and other controls

All these controls must be defined as one of two mutually exclusive styles:

  1. HORIZONTAL -- Layout the control horizontally.
  2. VERTICAL -- Layout the control vertically.

Spinners support some additional optional styles:

  • WRAP -- Wrap from high to low value.
  • READ_ONLY -- Do not allow typed input values.

ProgressBars support some additional optional styles:

  • SMOOTH -- Update is not in distinct steps.
  • INDETERMINATE -- There is no predetermined bound on the number of steps; the bar just keeps repeating over time.

To create these controls, use the code shown in listings 6-9. As described in "How to use combo, list, table, and tree controls," Java reflection is used, via the registerCallback method, to add SelectionListeners to these controls. This listener is invoked whenever the value of the control changes.


Listing 6. Methods to create a Slider

protected Slider createSlider(Composite parent, int style, 
                              int min, int current, int max, 
                              int inc, int pageinc, int thumb, 
                              String callback) {
    Slider s = new Slider(parent, style);
    if (min >= 0) {
        s.setMinimum(min);
    }
    if (max >= 0) {
        s.setMaximum(max);
    }
    if (current >= 0) {
        s.setSelection(current);
    }
    if (inc >= 1) {
        s.setIncrement(inc);
    }
    if (pageinc >= 1) {
        s.setPageIncrement(pageinc);
    }
    if (thumb >= 1) {
        s.setThumb(thumb);
    }
    if (callback != null) {
        registerCallback(s, this, callback);
    }
    return s;
}
protected Slider createVSlider(Composite parent, 
                               int min, int current, int max, 
                               int inc, int pageinc, int thumb, 
                               String callback) {
    return createSlider(parent, SWT.VERTICAL, min, current, max, 
                        inc, pageinc, thumb, callback);
}
protected Slider createHSlider(Composite parent, 
                               int min, int current, int max, 
                               int inc, int pageinc, int thumb, 
                               String callback) {
    return createSlider(parent, SWT.HORIZONTAL, min, current, max, 
                        inc, pageinc, thumb, callback);
}


Listing 7. Method to create a Spinner

protected Spinner createSpinner(Composite parent, int style, 
                                int min, int current, int max, 
                                int inc, int pageinc, String callback) {
    Spinner s = new Spinner(parent, style);
    if (min >= 0) {
        s.setMinimum(min);
    }
    if (max >= 0) {
        s.setMaximum(max);
    }
    if (current >= 0) {
        s.setSelection(current);
    }
    if (inc >= 1) {
        s.setIncrement(inc);
    }
    if (pageinc >= 1) {
        s.setPageIncrement(pageinc);
    }
    if (callback != null) {
        registerCallback(s, this, callback);
    }
    return s;
}


Listing 8. Method to create a Scale

protected Scale createScale(Composite parent, int style, 
                            int min, int current, int max, 
                            int inc, int pageinc) {
    Scale s = new Scale(parent, style);
    if (min >= 0) {
        s.setMinimum(min);
    }
    if (max >= 0) {
        s.setMaximum(max);
    }
    if (current >= 0) {
        s.setSelection(current);
    }
    if (inc >= 1) {
        s.setIncrement(inc);
    }
    if (pageinc >= 1) {
        s.setPageIncrement(pageinc);
    }
    return s;
}
protected Scale createVScale(Composite parent, 
                             int min, int current, int max, 
                             int inc, int pageinc) {
    return createScale(parent, SWT.VERTICAL, min, current, max, 
                       inc, pageinc);
}
protected Scale createHScale(Composite parent, 
                             int min, int current, int max, 
                             int inc, int pageinc) {
    return createScale(parent, SWT.HORIZONTAL, min, current, max, 
                       inc, pageinc);
}


Listing 9. Method to create a ProgressBar

protected ProgressBar createProgressBar(Composite parent, int style, 
                                        int min, int current, int max) {
    ProgressBar pb = new ProgressBar(parent, style);
    if (min >= 0) {
        pb.setMinimum(min);
    }
    if (max >= 0) {
        pb.setMaximum(max);
    }
    if (current >= 0) {
        pb.setSelection(current);
    }
    return pb;
}
protected ProgressBar createVProgressBar(Composite parent, 
                                         int min, int current, int max) {
    return createProgressBar(parent, SWT.VERTICAL, min, current, max);
}
protected ProgressBar createHProgressBar(Composite parent, 
                                         int min, int current, int max) {
    return createProgressBar(parent, SWT.HORIZONTAL, min, current, max);
}

You can query or set the current value of these controls. Consider the thread defined in Listing 10, which updates the label, progress bar and the slider in Figure 4. This thread is enabled when the "Automatic Update" Button (known as modeButton in the code) is selected.


Listing 10. Thread to update controls

protected class BarUpdater extends Thread {
    protected int delay;
    protected Display display;

    public BarUpdater(Display display) {
        this.display = display;
    }

    public void run() {
        try {
            while (true) {
                try {
                    if (!display.isDisposed()) {
                        display.syncExec(new Runnable() {
                                public void run() {
                                    if (!modeButton.isDisposed() &&
                                        !scale.isDisposed()) {
                                        delay = modeButton.getSelection() 
                                               ? scale.getSelection() : -1;
                                    }
                                }
                        });
                        if (delay >= 0) {
                            Thread.sleep(delay);
                            if (!display.isDisposed()) {
                                display.syncExec(new Runnable() {
                                    public void run() {
                                        if (!bar.isDisposed()) {
                                            int v = bar.getSelection() + 1;
                                            if (v > bar.getMaximum()) {
                                                v = bar.getMinimum();
                                            }
                                                    bar.setSelection(v);
                                            if (!slider.isDisposed()) {
                                                slider.setSelection(v);
                                            }
                                            if (!valueLabel.isDisposed()) {
                                                valueLabel.setText(
                                                       Integer.toString(v));
                                            }
                                        }
                                    }
                                });
                            }
                        }
                    }
                    Thread.sleep(100);
                }
                catch (InterruptedException ie) {
                }
            }
        }
        catch (Exception e) {
             e.printStackTrace();
        }
    }
}

Note that this code is careful to check to see if the various controls have been disposed before they are used. This is critical in asynchronous GUI manipulation. Also notice that all GUI access is done inside a syncExec (or its cousin asyncExec) method. This is needed whenever a GUI is accessed on a different thread from the one the GUI was created on.


StyledText

As described in the first installment, "How to create a simple SWT application," SWT supports plain text entry and display through the Text control. For more advanced text presentation, where fonts and colors need to be defined, use the StyledText control found in the custom package. StyledText is the control used by many Eclipse editors. Consider the sample of styled text shown in Figure 5. This text includes different colors and font modification, such as underline, strikeout, bold, and italic text. Note that strikeout and underline are supported only on Eclipse V3.1.


Figure 5. StyledText examples
Custom Tab Folder showing the Styled Text tab showing text with multiple styles

StyledText must be defined as one of two mutually exclusive styles:

  1. MULTI -- Displays multiple lines.
  2. SINGLE -- Displays a single line.

StyledText supports some additional optional styles:

  • WRAP -- Wrap lines at the right edge of the control.
  • READ_ONLY -- Do not allow typed input values.

Listing 11 shows the code to create a StyledText. Listing 12 shows its use with simple XML-like language to define the ranges of text with attributes.


Listing 11. Method to create a StyledText

protected StyledText createStyledText(Composite parent, int style) {
    return new StyledText(parent, style);
}


Listing 12. StyledText Example

styledText = createStyledText(body, SWT.MULTI | SWT.WRAP | 
                                    SWT.FULL_SELECTION);
styledText.addMouseListener(new MouseAdapter() {
    public void mouseDown(MouseEvent e) {
        if (e.button == 3) {
            processPopup();
        }
    }});
TextContent tc = new TextContent(body.getDisplay());
tc.setContent(dummyContent);
styledText.setText(tc.toPlainText());
styledText.setStyleRanges(tc.getStyleRanges());
 :
protected static final String dummyContent = 
"Just plain text!\n" +
"Now is the time for <b>all</b> good men " + 
"to come to the aid of their country\n" +
"<red><i>To <b>be</b></i> or <i>not to <b>be</b></i>?</red> " + 
"<blue><u>That</u> is the <so>question</so></blue>\n" +
"That's all folks!\n";

The StyledText example uses a helper class, TextContent, to identify ranges of text with special attributes. This class, included in the sample code, has support to parse the document and get its plain text content and find the various ranges within that plain text content with different attributes or attribute combinations. It creates an array of the SWT class StyleRange that represent these ranges. StyleRange has several fields that describe the range including foreground and background color, start offset and length and the attributes to apply, such as font style (bold or italic), strikeout, and underline.

StyledText has more function than shown here. For example, when you enter text into the document, it has no attributes, even if entered at an attributed point. You can make use of the features of StyledText to correct this behavior.


PopupList

There are times when you want to have the ability to display a list of choices as a pop-up window without creating a pop-up menu. The PopupList control can be used to do this. Figure 6 shows use of this control to add select, cut, copy, and paste functions to the text editor of Figure 5.


Figure 6. PopupList example
Example Popup List showing four choices: Select All, Cut, Copy, Paste

Listing 13 shows the content of the processPopup method. Note the code to position the pop-up relative to the control it relates to (in other words, the styled text).


Listing 13. PopupList Example

PopupList popup = createPopupList(shell, 50, popupItems);
popup.select(popupItems[0]);
Point p = styledText.getLocation();
p = shell.getDisplay().map(styledText, null, p.x, p.y);
String choice = popup.open(new Rectangle(
                    p.x + 100, p.y - 100, 100, 200));
if (choice != null) {
    if      (popupItems[0].equals(choice)) {
        styledText.selectAll();
    }
    else if (popupItems[1].equals(choice)) {
        styledText.cut();
    }
    else if (popupItems[2].equals(choice)) {
        styledText.copy();
    }
    else if (popupItems[3].equals(choice)) {
        styledText.paste();
    }
}
:
protected static final String[] popupItems = {
    "Select All", "Cut", "Copy", "Paste"
};


StackLayout

In the first two installments, I discussed several of the layout managers supplied with SWT, including the FillLayout, GridLayout, and FormLayout. The custom package provides the StackLayout, which can be used to place multiple GUIs on top of each other with only one GUI showing at a time (something like a TabFolder without the tabs). Consider Figure 7 and Figure 8 that show two states of a stack layout showing numbered labels. The stack is advanced by the ">>" button and retreated by the "<<" button. Figure 8 shows the layout after pressing ">>" four times.


Figure 7. StackLayout example 1
Example use of Stack Layout showing an initial label

Figure 8. StackLayout example 2
Example use of Stack Layout showing a final label

This stack was created by the code in Listing 14.


Listing 14. StackLayout Example

StackLayout stackLayout = new StackLayout();
Composite clabels = createComposite(body, SWT.BORDER, 
                                    stackLayout);
Label[] labels = new Label[5];
:
for (int i = 0; i < labels.length; i++) {
    Label xlabel = new Label(clabels, SWT.CENTER);
    xlabel.setText("Stack " + i);
    labels[i] = xlabel;
}
stackLayout.topControl = labels[0];
:
protected Composite createComposite(Composite parent, 
                                    int style, 
                                    Layout layout) {
    Composite c =  new Composite(parent, style);
    if (layout != null) {
        c.setLayout(layout);
    }
    return c;
}
protected Composite createComposite(Composite parent, 
                                    Layout layout) {
    return createComposite(parent, SWT.NONE, layout);
}

Listing 15 shows the code to advance to the next stack via the ">>" button. The code for the "<<" button is similar.


Listing 15. Code to advance to the next stack

protected int currentLabel;

public void doNext() {
    ++currentLabel;
    if (currentLabel >= labels.length) {
        currentLabel = 0;
    }
    stackLayout.topControl = labels[currentLabel];
    clabels.layout();
}


Conclusion

In this third installment of A gentle introduction to SWT and JFace, I have introduced many more SWT Controls, such as Tree for creating tabular trees; Canvas for drawing pictures; Slider, Scale, and Spinner for entering numeric values; ProgressBar for displaying progress; StyledText for entry of text with attributes; and PopupList for simple dynamic menus. I have also shown how to use the StackLayout to create time-wise overlapping GUIs. The next installment will show you how to use more SWT controls.



Download

DescriptionNameSizeDownload method
Sample applications and helper classos-jface3Samples-SWT3.ZIP11KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the author

Barry Feigenbaum

Barry Feigenbaum, Ph.D., is a member of the IBM Worldwide Accessibility Center, which helps IBM make its products accessible to people with disabilities. He has published several books and articles, holds several patents, and has spoken at industry conferences, such as JavaOne. He has served as an adjunct assistant professor of computer science at the University of Texas at Austin and other schools.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Java technology
ArticleID=94410
ArticleTitle=A gentle introduction to SWT and JFace, Part 3: How to use TabFolder, Canvas, and StyledText
publish-date=09272005
author1-email=feigenba@us.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers