Category Archives: Wicket

Special implementation for wicket programmers

Map tab key to indent and shift+tab to outdent in TinyMCE (in Wicket)

Today’s post is about changing the behavior of the TinyMCE implementation in the wicket stuff library. In this library TinyMCE functionality can be implemented by just adding the TinyMCEBehaviour to the wicket component TextArea and calling the behavior’s constructor with an instance of TinyMCESettings, where you can set all kind of parameters.

Generally very content with our implementation, our customer wanted a way to define pre-formatted text blocks to use in documents at a later point. For ease of use, pressing the tab key should indent the text and shift+tab should revert the effect = outdent the current line. Although the advanced edit settings showed indent/outdent buttons we also wanted it to work when the buttons aren’t shown, in forms where space is precious for example. After some google searches I found this post (cached google version), about how to execute TinyMCE functionality in javascript and this stackoverflow answer, about where to put this info.

The solution, which works pretty well for us, was to insert a method in our TinyMCE class, which just adds the following function to the settings instance.

public static TinyMCESettings addTabFunctionality(TinyMCESettings settings) {
settings.addCustomSetting("setup : function(ed) { " +
"ed.onKeyDown.add(function(ed, e) { " +
"if(e.keyCode == 9) {" +
"if(e.shiftKey) {" +
"ed.execCommand('Outdent');" +
"}else{" +
"ed.execCommand('Indent');" +
"}" +
"e.preventDefault();" +
"return false;" +
"}" +
"});" +
"}");
return settings;
}

The addCustomSetting() method adds code to the init() function created by the TinyMCE addon and in the javascript code we listen for keydown in the textarea. If keycode equals ‘9’ (tab) we check if shift is also pressed, if so an outdent action is performed using execCommand(‘Outdent’), if not, an indent is executed. By calling preventDefault() afterwards, the default action of the tab key is prevented – in our case it just was a 4 space-tab, which wasn’t written to the created html code. Calling the TinyMCE indent and outdent commands, padding is added to the affected line’s <p> tag (30px per tab) so the format can be saved to a database, without loosing the tabbed white-spaces.

Hope this helps someone, have a nice weekend! Until next time, feedback’s very welcome!

Building a custom FeedbackPanel in Wicket with Javascript

Today I’d like to present to you my first, very own tutorial about how to build a customized feedback-/notificationpanel in Wicket. I did this during my job a few weeks ago, because we were frustrated by the existing placement of our feedback panels. This one lets us show the notification anywhere on the page, while still using the Wicket built-in feedback system.

As far as the design goes, I managed to color the frame representing the most grave notification message (in our case just: info – warning – error) and color the distinct lines according to the level of feedback (blue for info, orange for warning, red for error). Sticking kinda to convention here 🙂

For every additional message the notification will stay half a second longer before fading out; this can be configured in the javascript function.

Implementation

Java

At first we will look at the java class of the notificationpanel, which extends FeedbackPanel to access all the built in methods of this class. If you want to know more details about the code please refer to the inline comments or just try it out, by copying it into your IDE.

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;

public class NotificationPanel extends FeedbackPanel {

	private static final long serialVersionUID = 1L;
	private final String cssClass = "notificationpanel";
	private String additionalCSSClass = "notificationpanel_top_right";

	// Create a notifcation panel with the default additional class, specified as a field variable
	public NotificationPanel(String id) {
		super(id);

		init(id, additionalCSSClass);
	}

	// Create a notifcation panel with a custom additional class, overwriting the field variable
	public NotificationPanel(String id, String additionalCSSClass) {
		super(id);

		this.additionalCSSClass = additionalCSSClass;

		init(id, additionalCSSClass);
	}

	private void init(String id, String additionalCSSClass) {
		// set custom markup id and ouput it, to find the component later on in the js function
		setMarkupId(id);
		setOutputMarkupId(true);

		// Add the additional cssClass and hide the element by default
		add(new AttributeModifier("class", true, new Model<String>(cssClass + " " + additionalCSSClass)));
                add(new AttributeModifier("style", true, new Model<String>("opacity: 0;")));
	}

	/**
	 * Method to refresh the notification panel
	 * 
	 * if there are any feedback messages for the user, find the gravest level, 
	 * format the notification panel accordingly and show it
	 * 
	 * @param target
	 *            AjaxRequestTarget to add panel and the calling javascript function
	 */
	public void refresh(AjaxRequestTarget target) {

		// any feedback at all in the current form?
		if (anyMessage()) {
			int highestFeedbackLevel = FeedbackMessage.INFO;

			// any feedback with the given level?
			if (anyMessage(FeedbackMessage.WARNING))
				highestFeedbackLevel = FeedbackMessage.WARNING;
			if (anyMessage(FeedbackMessage.ERROR))
				highestFeedbackLevel = FeedbackMessage.ERROR;

			// add the css classes to the notification panel, 
			// including the border css which represents the highest level of feedback
			add(new AttributeModifier("class", true,
                                 new Model<String>(cssClass
                                           + " " + additionalCSSClass
                                           + " notificationpanel_border_" + String.valueOf(highestFeedbackLevel))));

			// refresh the panel and call the js function with the panel markup id 
			// and the total count of messages
			target.addComponent(this);
			target.appendJavascript("showNotification('" + getMarkupId() + "', "
					+ getCurrentMessages().size() + ");");
		}
	}

	/**
	 * Returns css class for the single rows of the panel
	 * 
	 * @see org.apache.wicket.markup.html.panel.FeedbackPanel#getCSSClass(org.apache.wicket.feedback.FeedbackMessage)
	 */
	@Override
	protected String getCSSClass(FeedbackMessage message) {
		return "notificationpanel_row_" + message.getLevelAsString();
	}
}

Javascript

The javascript function uses the jquery fading methods to show and hide the notification in a nice manor. Here you can configure the time intervals and the opacity of the fading effects.

function showNotification(componentId, messagecount){
	
	timeout = 5000 + (messagecount * 500);
	
	$('div#' + componentId).fadeTo('normal', 1.0);
	
	setTimeout("$('div#" + componentId + "').fadeTo('normal', 0.6)", timeout);
	
	timeout += 2000; 
	
	setTimeout("$('div#" + componentId + "').fadeOut('normal')", timeout);
}

CSS

The css classes used. The class notifcationpanel defines some standard properties like bg color, padding, rounded corners (css3 border-radius) and position: fixed to maintain an absolute position and don’t scroll with the content. .notificationpanel_xxx are the classes used to add as the additionalCSSClass to the NotificationPanel, here you can add your custmized css class. .notificationpanel_border/row_xxx defines the border and colors of the panel and the distinct messages, configure as you please.

.notificationpanel {
background-color: #fff;
min-width:150px;
padding: 3px;
position: fixed;
z-index:999;
border-radius:8px;
}

.notificationpanel_bottom_left {
bottom: 100px;
left: 30px;
}

.notificationpanel_bottom_detail_form {
top: 540px;
right: 30px;
}

.notificationpanel_bottom_right {
bottom: 100px;
right: 30px;
}

.notificationpanel_top_right {
top: 15px;
right: 30px;
}

/* info */
.notificationpanel_border_200 {
	border: 3px solid #0053a0;
	box-shadow: 0px 0px 5px #000;
}

.notificationpanel_row_INFO {
	color: #0053a0;
}

/* warning */
.notificationpanel_border_300 {
	border: 3px solid #FF9900;
	box-shadow: 0px 0px 6px #000;
}

.notificationpanel_row_WARNING {
	color: #FF9900;
}

/* error */
.notificationpanel_border_400 {
	border: 3px solid #CC3300;
	box-shadow: 0px 0px 7px #000;
}

.notificationpanel_row_ERROR {
	color: #CC3300;
}

Usage

HTML

To use the NotificationPanel just define a div container with a wicket:id of your choosing and watch the hierarchy to add it to the right component (works best inside a form).

Java

After that you can create the notificationPanel in your java code, be sure to create the variable as global as you need it or pass it on as a parameter, to the parts where you want to use it.

//leave out second parameter if you like to create a panel which uses the additional css class defined in the NotificationPanel class variable
NotificationPanel notificationPanel = new NotificationPanel("notificationPanelId", "notificationpanel_bottom_detail_form");
add(notificationPanel);

When all is set up, you can use the panel quite like the standard Wicket FeedbackPanel. Just define your info/warning/error message and, here’s the small difference, instead of just adding the panel to the AjaxRequestTarget, call the refresh method of the panel. Here you go!

error("Random error message!!!");
notificationPanel.refresh(ajaxRequestTarget);

I hope you’ve enjoyed my first tutorial and I hope I can help somebody, dealing with a similar problem as we have! If you like this post, please consider subscribing to my feed or following me in any other way; see Follow Me! on the right for your choice 😉

I’d be also very happy, if you drop me a line in the comments or contact me any other way, be it for criticism, a simple comment, thanks, suggestions for improvement, or else! Thank you for reading!

Update 10/21/2011: – Refactored the AttributeModifiers, which weren’t part of stock wicket. I tested it in another project with the current code, works now!