Java tutorial
/** * Copyright 2010 David Froehlich <david.froehlich@businesssoftware.at>, Samuel * Kogler <samuel.kogler@gmail.com>, Stephan Stiboller <stistc06@htlkaindorf.at> * * This file is part of Codesearch. * * Codesearch is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * Codesearch is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * Codesearch. If not, see <http://www.gnu.org/licenses/>. */ package org.codesearch.searcher.client.ui.fileview; import java.util.LinkedList; import java.util.List; import org.codesearch.searcher.client.ClientFactory; import org.codesearch.searcher.client.ui.fileview.sidebar.Sidebar; import org.codesearch.searcher.client.ui.fileview.sidebar.SidebarImpl; import org.codesearch.searcher.shared.OutlineNode; import org.codesearch.searcher.shared.SidebarNode; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.DivElement; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.HasClickHandlers; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.resources.client.CssResource; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.SplitLayoutPanel; import com.google.gwt.user.client.ui.TabLayoutPanel; import com.google.gwt.user.client.ui.Widget; /** * Implementation of the File View. * * @author Samuel Kogler */ public class FileViewImpl extends Composite implements FileView { // TODO cleanup this class interface FileViewUiBinder extends UiBinder<Widget, FileViewImpl> { } interface MyStyle extends CssResource { String hidden(); } @UiField MyStyle style; @UiField FlowPanel fileContentContainer; @UiField FlowPanel lineNumbersContainer; @UiField SplitLayoutPanel splitWrapper; @UiField ScrollPanel scrollWrapper; @UiField Label filenameLabel; @UiField Label repositoryField; @UiField HasClickHandlers sidebarButton; @UiField DivElement focusDiv; @UiField TabLayoutPanel sidebarTabPanel; /** * UiBinder template. */ private static FileViewUiBinder uiBinder = GWT.create(FileViewUiBinder.class); /** * The presenter for this view. */ private Presenter presenter; /** * Handler for keyboard shortcuts. */ private HandlerRegistration keyboardShortcutHandlerRegistration; /** * Number of lines of the displayed file */ private int lineCount = 0; /** * Whether or not the focus line div is visible. */ private boolean focusDivVisible; private String searchTerm; /** * Whether or not the sidebar is visible. */ private boolean sidebarVisible = false; /** * The list of displayed sidebars in this view. */ private List<Sidebar> shownSidebars = new LinkedList<Sidebar>(); public FileViewImpl() { initWidget(uiBinder.createAndBindUi(this)); exportJSFunctions(); showSidebar(false); } @UiHandler("goToLineButton") void onGoToLine(ClickEvent event) { goToLineWithDialog(); } @UiHandler("sidebarButton") void onSidebar(ClickEvent event) { toggleSidebar(); } /** * {@inheritDoc} */ @Override public void cleanup() { fileContentContainer.clear(); lineNumbersContainer.clear(); shownSidebars.clear(); updateSidebar(); lineCount = 0; sidebarVisible = false; showSidebar(sidebarVisible); showFocusDiv(false); } /** * {@inheritDoc} */ @Override public void setPresenter(FileView.Presenter presenter) { this.presenter = presenter; } /** * {@inheritDoc} */ @Override public Presenter getPresenter() { return presenter; } /** * {@inheritDoc} */ @Override public void setFileContent(String fileContent, boolean binary) { lineNumbersContainer.setVisible(!binary); showFocusDiv(false); if (!binary) { lineCount = fileContent.split("\n").length; for (int i = 0; i < lineCount; i++) { Label lineNumber = new Label(String.valueOf(i + 1)); lineNumber.addClickHandler(new LineNumberClickHandler(i + 1)); lineNumbersContainer.add(lineNumber); } } fileContent = "<pre id='fileContent'>" + fileContent + "</pre>"; HTML html = new HTML(fileContent); fileContentContainer.add(html); if (searchTerm != null && !searchTerm.isEmpty()) { for (String currentTerm : parseHighlightTerms(searchTerm)) { highlight("#fileContent", currentTerm); } } } private List<String> parseHighlightTerms(String searchTerm) { List<String> highlightTerms = new LinkedList<String>(); String[] termParts = searchTerm.split("\""); String currentTerm; for (int i = 0; i < termParts.length; i++) { currentTerm = termParts[i]; //only parse even terms, in order to ignore terms between " currentTerm = currentTerm.replace(": ", ":"); //it's ugly and I know it for (String subPart : currentTerm.split(" ")) { if (i % 2 == 0) { if (!subPart.contains(":") && !subPart.trim().isEmpty()) { highlightTerms.add(subPart.replace("*", "").replace("~", "").replace("?", "")); } else { highlightTerms.add(currentTerm); } } } } return highlightTerms; } private native void highlight(String selector, String term)/*-{ $wnd.jQuery(selector).highlight(term); }-*/; /** * {@inheritDoc} */ @Override public void setSearchTerm(String searchTerm) { this.searchTerm = searchTerm; } /** * {@inheritDoc} */ @Override public void setOutline(List<OutlineNode> outline) { if (outline != null) { Sidebar sidebar = new SidebarImpl(); sidebar.setTitle("Outline"); for (SidebarNode s : outline) { sidebar.add(s); } sidebar.expandAll(); shownSidebars.add(sidebar); updateSidebar(); if (!sidebarVisible) { toggleSidebar(); } } } /** * {@inheritDoc} */ @Override public void setFilePath(String filePath) { if (filePath != null) { String fileName = filePath; if (fileName.contains("/")) { fileName = fileName.substring(filePath.lastIndexOf('/') + 1); } filenameLabel.setText(fileName); filenameLabel.setTitle(filePath); Window.setTitle(filePath); } } /** * {@inheritDoc} */ @Override public void setRepository(String repository) { repositoryField.setText(repository); } /** * {@inheritDoc} */ @Override public void goToLine(int lineNumber) { if (lineNumber > 0 && lineNumber <= lineCount) { if (!focusDivVisible) { showFocusDiv(true); } int position = (lineNumber - 1) * 15; focusDiv.setAttribute("style", "top: " + position + "px"); // scroll all possible parents focusDiv.scrollIntoView(); // scroll wrapper manually int scrollPosition = position - scrollWrapper.getElement().getClientHeight() / 2; scrollPosition = Math.min(scrollPosition, scrollWrapper.getMaximumVerticalScrollPosition()); scrollWrapper.setVerticalScrollPosition(scrollPosition); } } /** * {@inheritDoc} */ @Override public void connectEventHandlers() { keyboardShortcutHandlerRegistration = Event.addNativePreviewHandler(new NativePreviewHandlerImpl()); } /** * {@inheritDoc} */ @Override public void disconnectEventHandlers() { keyboardShortcutHandlerRegistration.removeHandler(); } public static void staticGoToUsage(int usageId) { ClientFactory.getDefaultFactory().getFileView().getPresenter().goToUsage(usageId); } public static void staticGoToLine(int lineNumber) { ClientFactory.getDefaultFactory().getFileView().goToLine(lineNumber); } /** * Exports static functions to JavaScript so they can be used from HTML * code. */ public static native void exportJSFunctions()/*-{ $wnd.goToLine = $entry(@org.codesearch.searcher.client.ui.fileview.FileViewImpl::staticGoToLine(I)); $wnd.goToUsage = $entry(@org.codesearch.searcher.client.ui.fileview.FileViewImpl::staticGoToUsage(I)); }-*/; private void toggleSidebar() { sidebarVisible = !sidebarVisible; showSidebar(sidebarVisible); } private void showSidebar(boolean show) { if (show) { splitWrapper.clear(); splitWrapper.addEast(sidebarTabPanel, 300); splitWrapper.add(scrollWrapper); } else { splitWrapper.remove(sidebarTabPanel); } splitWrapper.animate(50); } private void updateSidebar() { sidebarTabPanel.clear(); for (Sidebar sidebar : shownSidebars) { sidebarTabPanel.add(sidebar, sidebar.getTitle()); } } /** * Shows an input dialog for the go to line feature. */ private void goToLineWithDialog() { String input = Window.prompt("Go to line:", ""); try { int lineNumber = Integer.parseInt(input); goToLine(lineNumber); } catch (NumberFormatException ex) { } } /** * Shows or hides the focused line div that highlights the focused line. * * @param show Whether to show the div */ private void showFocusDiv(boolean show) { focusDivVisible = show; if (show) { focusDiv.removeClassName(style.hidden()); } else { focusDiv.addClassName(style.hidden()); } } /** * Handler class that intercepts native javascript events. Used for global * hotkeys. */ private class NativePreviewHandlerImpl implements NativePreviewHandler { public NativePreviewHandlerImpl() { } /** * {@inheritDoc} */ @Override public void onPreviewNativeEvent(NativePreviewEvent event) { switch (event.getNativeEvent().getCharCode()) { case 'g': event.cancel(); event.consume(); goToLineWithDialog(); break; } } } /** * Click handler that is used by line numbers to call the goToLine function. */ private class LineNumberClickHandler implements ClickHandler { private int targetLine; public LineNumberClickHandler(int targetLine) { this.targetLine = targetLine; } /** * {@inheritDoc} */ @Override public void onClick(ClickEvent event) { goToLine(targetLine); } } }