Repository: axaluss/power-mode-intellij-plugin Branch: master Commit: b95c63edd88b Files: 41 Total size: 204.4 KB Directory structure: gitextract_08n_przv/ ├── .gitignore ├── .scalafmt.conf ├── LICENSE ├── README.md ├── build.sbt ├── log4j.xml ├── project/ │ ├── build.properties │ └── plugins.sbt ├── resources/ │ └── META-INF/ │ └── plugin.xml └── src/ └── main/ ├── java/ │ ├── de/ │ │ └── ax/ │ │ └── powermode/ │ │ └── power/ │ │ ├── color/ │ │ │ ├── ColorViewController.java │ │ │ ├── MultiGradientPanel.java │ │ │ ├── UiTest.form │ │ │ └── UiTest.java │ │ └── ui/ │ │ ├── PowerModeConfigurableUI.form │ │ ├── PowerModeConfigurableUI.java │ │ └── ValueSettable.java │ └── javazoom/ │ └── jl/ │ └── player/ │ └── HackyJavaSoundAudioDevice.java └── scala/ └── de/ └── ax/ └── powermode/ ├── ImageUtil.scala ├── MyCaretListener.scala ├── MyTypedActionHandler.scala ├── NextSongAction.scala ├── Power.scala ├── PowerMode.scala ├── PowerModeConfigurable.scala ├── PowerModeStartup.scala ├── TogglePowerMode.scala ├── Util.scala ├── cache/ │ └── Cache.scala ├── package.scala └── power/ ├── ElementOfPower.scala ├── color/ │ ├── ColorEdges.scala │ └── MyPaint.scala ├── element/ │ ├── PowerBam.scala │ ├── PowerFlame.scala │ ├── PowerIndicator.scala │ └── PowerSpark.scala ├── hotkeys/ │ └── HotkeyHeatupListener.scala ├── management/ │ ├── ElementOfPowerContainer.scala │ └── ElementOfPowerContainerManager.scala └── sound/ ├── MediaPlayer.scala └── PowerSound.scala ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .idea out *.iml *.jar lib/ *.zip **/target .bsp/ ================================================ FILE: .scalafmt.conf ================================================ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ Power Mode II Intellij plugin ====================== [](https://gitter.im/baptistemesta/power-mode-intellij-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)  Installation -------------- [Watch the Video](https://www.youtube.com/watch?v=aIWs7YQ9aMs) [Install from Jetbrains plugin repositories](https://plugins.jetbrains.com/plugin/8251) How to use --------- Install the plugin then simply enable the sparkling in Preferences > Appearance > Power mode II ## Features
Want to Donate a Beer?
## Plugin Development Setup
1. install scala intellij plugin
2. import the sbt project (`build.sbt`)
3. follow the docs [here](https://github.com/JetBrains/sbt-idea-plugin)
4. create the plugin artifact `sbt packageArtifactZip`
5. install in intellij
## "Architecture"
* `PowerMode` is the settings instance which is used by `PowerModeConfigurable` to populate the UI Settings dialog.
`PowerModeConfigurableUI` manages the settings dialog to change the settings in `PowerMode`.
* `PowerMode` is stored/loaded by xml serializer and annotation magic by Intellij.
* `PowerMode` starts up the `SparkContainerManager`. The `SparkContainerManager` creates a `SparkContainer` for each
editor.
* The `SparkContainer` creates the `ElementOfPower` (`PowerSpark, PowerFlame`) and manages their animation and
lifecycle.
### heatup
* heatup increases the lifetime and amount of Sparks and Flames over time. The most values are multiplied
with `ElementOfPower.valueFactor` to simulate this heatup.
`ElementOfPower.valueFactor` is composed of `heatupFactor` (how much of all animation doesn't depend on heatup)
and `timeFactor` (how much heatup do we currently have).
* The heatup itself is calculated by `keyStrokesPerMinute` and the amount of keystrokes within `heatupTime`.
================================================
FILE: build.sbt
================================================
import org.jetbrains.sbtidea.Keys._
lazy val powerMode =
project
.in(file("."))
.enablePlugins(SbtIdeaPlugin)
.settings(
version := "105.501",
scalaVersion := "2.13.12",
ThisBuild / intellijPluginName := "Power Mode II",
ThisBuild / intellijBuild := "232.10227.8",
ThisBuild / intellijPlatform := IntelliJPlatform.IdeaCommunity,
Global / intellijAttachSources := true,
Compile / javacOptions ++= "--release" :: "17" :: Nil,
intellijPlugins += "com.intellij.properties".toPlugin,
libraryDependencies ++= Seq(
"javazoom" % "jlayer" % "1.0.1",
"org.typelevel" %% "squants" % "1.6.0",
"org.apache.commons" % "commons-math3" % "3.3",
"com.eclipsesource.minimal-json" % "minimal-json" % "0.9.5" withSources ()
),
libraryDependencies += "com.ryanstull" %% "scalanullsafe" % "1.2.6",
scalacOptions in ThisBuild += "-deprecation",
unmanagedJars in Compile += baseDirectory.value / "lib",
unmanagedResourceDirectories in Compile += baseDirectory.value / "resources",
resourceDirectories in Runtime += baseDirectory.value / "resources",
unmanagedResourceDirectories in Test += baseDirectory.value / "testResources",
packageMethod := PackagingMethod.Standalone(),
bundleScalaLibrary in ThisBuild := true
)
================================================
FILE: log4j.xml
================================================
Want to pass me a Beer?
]]>JavaSoundAudioDevice implements an audio
* device by using the JavaSound API.
*
* @author Mat McGowan
* @since 0.0.8
*/
public class HackyJavaSoundAudioDevice extends AudioDeviceBase {
public SourceDataLine source = null;
public AudioFormat fmt = null;
public byte[] byteBuf = new byte[4096];
protected void setAudioFormat(AudioFormat fmt0) {
fmt = fmt0;
}
protected AudioFormat getAudioFormat() {
if (fmt == null) {
Decoder decoder = getDecoder();
fmt = new AudioFormat(decoder.getOutputFrequency(),
16,
decoder.getOutputChannels(),
true,
false);
}
return fmt;
}
protected DataLine.Info getSourceLineInfo() {
AudioFormat fmt = getAudioFormat();
//DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt, 4000);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt);
return info;
}
public void open(AudioFormat fmt) throws JavaLayerException {
if (!isOpen()) {
setAudioFormat(fmt);
openImpl();
setOpen(true);
}
}
protected void openImpl()
throws JavaLayerException {
}
// createSource fix.
protected void createSource() throws JavaLayerException {
Throwable t = null;
try {
Line line = AudioSystem.getLine(getSourceLineInfo());
if (line instanceof SourceDataLine) {
source = (SourceDataLine) line;
//source.open(fmt, millisecondsToBytes(fmt, 2000));
source.open(fmt);
/*
if (source.isControlSupported(FloatControl.Type.MASTER_GAIN))
{
FloatControl c = (FloatControl)source.getControl(FloatControl.Type.MASTER_GAIN);
c.setValue(c.getMaximum());
}*/
source.start();
}
} catch (RuntimeException ex) {
t = ex;
} catch (LinkageError ex) {
t = ex;
} catch (LineUnavailableException ex) {
t = ex;
}
if (source == null) throw new JavaLayerException("cannot obtain source audio line", t);
}
public int millisecondsToBytes(AudioFormat fmt, int time) {
return (int) (time * (fmt.getSampleRate() * fmt.getChannels() * fmt.getSampleSizeInBits()) / 8000.0);
}
protected void closeImpl() {
if (source != null) {
source.close();
}
}
protected void writeImpl(short[] samples, int offs, int len)
throws JavaLayerException {
if (source == null)
createSource();
byte[] b = toByteArray(samples, offs, len);
source.write(b, 0, len * 2);
}
protected byte[] getByteArray(int length) {
if (byteBuf.length < length) {
byteBuf = new byte[length + 1024];
}
return byteBuf;
}
protected byte[] toByteArray(short[] samples, int offs, int len) {
byte[] b = getByteArray(len * 2);
int idx = 0;
short s;
while (len-- > 0) {
s = samples[offs++];
b[idx++] = (byte) s;
b[idx++] = (byte) (s >>> 8);
}
return b;
}
protected void flushImpl() {
if (source != null) {
source.drain();
}
}
public int getPosition() {
int pos = 0;
if (source != null) {
pos = (int) (source.getMicrosecondPosition() / 1000);
}
return pos;
}
/**
* Runs a short test by playing a short silent sound.
*/
public void test()
throws JavaLayerException {
try {
open(new AudioFormat(22050, 16, 1, true, false));
short[] data = new short[22050 / 10];
write(data, 0, data.length);
flush();
close();
} catch (RuntimeException ex) {
throw new JavaLayerException("Device test failed: " + ex);
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/ImageUtil.scala
================================================
package de.ax.powermode
import com.intellij.util.PathUtil
import de.ax.powermode.cache.Cache
import de.ax.powermode.power.element.PowerFlame
import java.awt.image.BufferedImage
import java.io.{BufferedOutputStream, File, FileOutputStream, InputStream}
import java.net.{URI, URL}
import javax.imageio.{IIOException, ImageIO}
import scala.util.{Failure, Success, Try}
object ImageUtil {
def imagesForPath(folder: Option[File]): scala.List[BufferedImage] = {
val orElse = folder.getOrElse(new File("UNDEFINED"))
//logger.debug(s"imagesForPath $folder")
ImageUtil.listCache
.getOrUpdate(orElse) {
Some(folder.map(ImageUtil.images).toList.flatten.map(f => f()))
}
.toList
.flatten
}
val imageCache =
new Cache[URI, BufferedImage, Long](fname => new File(fname).lastModified(),
fname => !new File(fname).exists())
var lastUpdate = System.currentTimeMillis()
val listCache = new Cache[File, List[BufferedImage], File](
f => f,
f =>
if (System.currentTimeMillis() - 5000 > lastUpdate) {
lastUpdate = System.currentTimeMillis()
true
} else false)
def images(imagesPath: File): List[() => BufferedImage] = {
val imageUrls = getImageUrls(imagesPath)
getImagesCached(imageUrls).map(img => { () =>
ImageUtil.deepCopy(img)
})
}
import java.awt.image.BufferedImage
def deepCopy(bi: BufferedImage): BufferedImage = {
val cm = bi.getColorModel
val isAlphaPremultiplied = cm.isAlphaPremultiplied
val raster = bi.copyData(null)
new BufferedImage(cm, raster, isAlphaPremultiplied, null)
}
def getUrl(uri: URI): Try[URL] = Try {
uri.toURL
}
private def getImagesCached(imageUrls: List[URI]) = {
imageUrls.flatMap(uri =>
imageCache.getOrUpdate(uri) {
val maybeImg = Try {
Option(ImageIO.read(uri.toURL))
}
maybeImg match {
case Success(Some(img)) =>
val bufferedImage = new BufferedImage(img.getWidth,
img.getHeight,
BufferedImage.TYPE_INT_ARGB)
val graphics = bufferedImage.getGraphics
graphics.drawImage(img, 0, 0, null)
Some(bufferedImage)
case Failure(e) =>
e match {
case io: IIOException =>
PowerMode.logger
.info(
s"could not load image file! Please try to store your PowerMode " +
s"Images/Animations in a different folder and restart the application! File not found: '$uri'!",
e
)
case ex =>
PowerMode.logger
.info(
s"could not load image file! Please try to store your PowerMode " +
s"Images/Animations in a different folder and restart the application! File not found: '$uri'!",
ex
)
}
None
case Success(None) =>
PowerMode.logger
.info(s"could not load image from url '${uri.toURL}'")
None
}
})
}
private def getImageUrls(imagesPath: File): List[URI] = {
try {
//logger.debug(s"FILE: $imagesPath")
val urls = if (imagesPath.exists()) {
getFileImages(imagesPath)
} else if (debugFolderExists(imagesPath)) {
getImageUrlsFromDebugDir(imagesPath)
} else {
getImageUrlsFromResources(imagesPath)
}
//logger.debug(s"URLS: $urls")
urls
} catch {
case e: Throwable =>
PowerMode.logger.debug(
s"info getting image urls from '${imagesPath}': ${e.getMessage}",
e)
throw e
}
}
private def getFileImages(imagesPath: File): List[URI] = {
//logger.debug(s"LOADING FROM normal path: $imagesPath")
val files = if (imagesPath.isFile) {
List(imagesPath)
} else {
Option(imagesPath.getAbsoluteFile.listFiles())
.map(_.toList)
.toList
.flatten
.filter(_.isFile)
}
files.map(_.getAbsoluteFile.toURI)
}
private def debugFolderExists(imagesPath: File): Boolean = {
val file = new File(PathUtil.getJarPathForClass(classOf[PowerFlame]),
imagesPath.getPath)
file.exists()
}
private def getImageUrlsFromDebugDir(imagesPath: File): List[URI] = {
val file = new File(PathUtil.getJarPathForClass(classOf[PowerFlame]),
imagesPath.getPath)
//logger.debug(s"LOADING FROM exploded sandbox: $file")
Option(file.listFiles())
.map(_.toList)
.toList
.flatten
.filter(_.isFile)
.map(_.toURI)
}
def writeBytes(data: LazyList[Byte], file: File) = {
val target = new BufferedOutputStream(new FileOutputStream(file))
try data.foreach(target.write(_))
finally target.close()
}
lazy val fireUrls = (1 to 25)
.map(i => if (i > 9) s"$i" else s"0$i")
.map { i =>
mkTmpImg(
classOf[PowerFlame].getResourceAsStream(
s"/fire/animated/256/fire1_ $i.png"))
}
.toList
lazy val bamUrls =
Option(classOf[PowerFlame].getResourceAsStream(s"/bam/bam.png"))
.map(mkTmpImg)
.toList
private def getImageUrlsFromResources(imagesFolder: File): List[URI] = {
val loader = this.getClass().getClassLoader()
//logger.debug(s"LOADING FROM JAR: $imagesFolder")
val uRLs: List[URL] = if (imagesFolder.getPath.contains("fire")) {
fireUrls
} else if (imagesFolder.getPath.contains("bam")) {
bamUrls
} else {
None.toList
}
uRLs.map(_.toURI)
}
private def mkTmpImg(stream: InputStream): URL = {
import java.io.File
val tempFile =
File.createTempFile(s"${System.currentTimeMillis()}_pmtempfile_", ".png")
tempFile.deleteOnExit()
writeBytes(
LazyList.continually(stream.read).takeWhile(_ != -1).map(_.toByte),
tempFile)
tempFile.toURI.toURL
}
}
================================================
FILE: src/main/scala/de/ax/powermode/MyCaretListener.scala
================================================
package de.ax.powermode
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.event.{CaretEvent, CaretListener}
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.IntUnaryOperator
import scala.util.Try
/**
* Created by nyxos on 04.01.17.
*/
class MyCaretListener extends CaretListener with Power {
var modified = true
val caretCount = new AtomicInteger(1)
override def caretPositionChanged(caretEvent: CaretEvent): Unit = {
if (!modified && powerMode.caretAction) {
initializeAnimationByCaretEvent(caretEvent.getCaret)
}
modified = false
}
override def caretRemoved(caretEvent: CaretEvent): Unit = {
modified = true
caretCount.getAndUpdate((i: Int) => {
if (i > 2) {
i - 1
} else {
1
}
})
}
override def caretAdded(caretEvent: CaretEvent): Unit = {
modified = true
caretCount.getAndIncrement()
}
private def initializeAnimationByCaretEvent(caret: Caret): Unit = {
val isActualEditor = Try {
Util.isActualEditor(caret.getEditor)
}.getOrElse(false)
if (isActualEditor) {
Util
.getCaretPosition(caret)
.toOption
.foreach(p =>
powerMode.maybeElementOfPowerContainerManager.foreach(
_.initializeAnimation(caret.getEditor, p)))
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/MyTypedActionHandler.scala
================================================
package de.ax.powermode
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.TypedActionHandler
import java.awt.Point
import scala.jdk.CollectionConverters._
/**
* Created by nyxos on 04.01.17.
*/
class MyTypedActionHandler(typedActionHandler: TypedActionHandler)
extends TypedActionHandler
with Power {
def execute(editor: Editor, c: Char, dataContext: DataContext): Unit = {
if (powerMode.isEnabled) {
powerMode.increaseHeatup(dataContext = Option(dataContext))
if (!powerMode.caretAction) {
initializeAnimationByTypedAction(editor)
}
}
try {
typedActionHandler.execute(editor, c, dataContext)
} catch {
case x: IllegalStateException =>
logger.debug(x.getMessage, x)
case x: IndexOutOfBoundsException =>
logger.debug(x.getMessage, x)
}
}
def getEditorCaretPositions(editor: Editor): Seq[Point] = {
editor.getCaretModel.getAllCarets.asScala.toList
.map({ c =>
Util.getCaretPosition(editor, c)
})
.filter(_.isSuccess)
.map(_.get)
}
def initializeAnimationByTypedAction(editor: Editor): Unit = {
val isActualEditor = Util.isActualEditor(editor)
if (isActualEditor) {
val positions = getEditorCaretPositions(editor)
positions.foreach(pos => {
powerMode.maybeElementOfPowerContainerManager.foreach(
_.initializeAnimation(editor, pos))
})
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/NextSongAction.scala
================================================
package de.ax.powermode
import com.intellij.openapi.actionSystem.{AnAction, AnActionEvent}
/**
* Created by nyxos on 27.12.16.
*/
class NextSongAction extends AnAction with Power {
override def actionPerformed(anActionEvent: AnActionEvent): Unit = {
powerMode.maybeElementOfPowerContainerManager.foreach(cm => {
cm.sound.foreach(_.next())
})
}
}
================================================
FILE: src/main/scala/de/ax/powermode/Power.scala
================================================
package de.ax.powermode
/**
* Created by nyxos on 04.01.17.
*/
trait Power {
def powerMode: PowerMode = {
PowerMode.getInstance
}
def logger = PowerMode.logger
}
================================================
FILE: src/main/scala/de/ax/powermode/PowerMode.scala
================================================
/*
* Copyright 2015 Baptiste Mesta
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ax.powermode
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.{
ApplicationComponent,
PersistentStateComponent,
Service,
State,
Storage
}
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.actionSystem.{
EditorActionManager,
TypedAction
}
import com.intellij.util.xmlb.XmlSerializerUtil
import de.ax.powermode.PowerMode.logger
import de.ax.powermode.power.color.ColorEdges
import de.ax.powermode.power.management.ElementOfPowerContainerManager
import org.apache.commons.math3.stat.regression.SimpleRegression
import com.intellij.openapi.diagnostic.Logger
import de.ax.powermode.power.hotkeys.HotkeyHeatupListener
import org.jetbrains.annotations.Nullable
import squants.Dimensionless
import squants.DimensionlessConversions.{DimensionlessConversions, each}
import squants.time.Time._
import squants.time.Frequency._
import squants.time.TimeConversions._
import squants.time.FrequencyConversions._
import squants.MetricSystem._
import squants.time.{Frequency, Time, TimeUnit}
import scala.language.postfixOps
import java.awt.event.InputEvent
import java.io.File
import javax.swing.KeyStroke
import scala.collection.immutable.Seq
import scala.util.Try
/**
* @author Baptiste Mesta
*/
object PowerMode {
val logger: Logger = Logger.getInstance(classOf[PowerMode])
private var instance: PowerMode = null
@Nullable def getInstance: PowerMode = synchronized {
if (instance != null) {
instance
} else {
logger.debug("No instance!")
try {
instance =
ApplicationManager.getApplication.getService(classOf[PowerMode])
instance.initComponent()
instance
} catch {
case e: Throwable =>
logger.debug("info getting component: " + e.getMessage(), e)
null
}
}
}
def obtainColorEdges(pm: PowerMode): ColorEdges = {
import pm._
val edges = new ColorEdges()
edges.setAlpha(getColorAlpha)
edges.setRedFrom(getRedFrom)
edges.setRedTo(getRedTo)
edges.setGreenFrom(getGreenFrom)
edges.setGreenTo(getGreenTo)
edges.setBlueFrom(getBlueFrom)
edges.setBlueTo(getBlueTo)
edges
}
}
//@Service(Array(Service.Level.APP))
@State(name = "PowerModeII",
storages = Array(new Storage(file = "$APP_CONFIG$/power.mode.ii.xml")))
final class PowerMode
extends PersistentStateComponent[PowerMode]
with Disposable {
override def dispose(): Unit = {
maybeElementOfPowerContainerManager.foreach(_.dispose)
}
type Timestamp = Time
type CaretCount = Int
type HeatupKey = (Option[KeyStroke], Timestamp, CaretCount)
val mediaPlayerExists: Try[Class[_]] = Try {
Class.forName("javax.sound.sampled.SourceDataLine")
}
var isSingleBamImagePerEvent: Boolean = false
var hotkeyHeatup: Boolean = true
var bamLife: Time = 1000 milliseconds
var soundsFolder = Option.empty[File]
var minVolume: Dimensionless = 10.percent
var maxVolume: Dimensionless = 100.percent
def getMinVolume: Int = minVolume.toPercent.toInt
def setMinVolume(value: Int): Unit = {
minVolume = value.percent
logger.debug(s"Setting min volume ${minVolume}")
maxVolume = Seq(minVolume, maxVolume).max
}
def getMaxVolume: Int = maxVolume.toPercent.toInt
def setMaxVolume(value: Int): Unit = {
maxVolume = value.percent
logger.debug(s"Setting max volume ${maxVolume}")
minVolume = Seq(minVolume, maxVolume).min
}
var gravityFactor: Double = 21.21
var sparkVelocityFactor: Double = 4.36
var sparkSize = 3
var sparksEnabled = true
var frameRate: Frequency = 30 hertz
var maxFlameSize: Int = 100
var maxFlameLife: Time = 2000 milliseconds
var heatupTimeMillis: Time = 10000 milliseconds
var lastKeys = List.empty[HeatupKey]
var keyStrokesPerMinute: Frequency = 300 / 1.minutes
var heatupFactor = 1.0
var sparkLife = 3000
var sparkCount = 10
var shakeRange: Int = 4
var flamesEnabled: Boolean = true
var maybeElementOfPowerContainerManager
: Option[ElementOfPowerContainerManager] =
Option.empty
var isBamEnabled: Boolean = true
var isSoundsPlaying: Boolean = false
var powerIndicatorEnabled: Boolean = true
var caretAction: Boolean = false
var hotkeyWeight: Dimensionless = keyStrokesPerMinute * (3 seconds)
var redFrom: Int = 0
var redTo: Int = 255
var greenFrom: Int = 0
var greenTo: Int = 255
var blueFrom: Int = 0
var blueTo: Int = 255
var colorAlpha: Int = 164
var heatupThreshold: Double = 0.0
var _isCustomFlameImages: Boolean = false
var _isCustomBamImages: Boolean = false
var customFlameImageFolder = Option.empty[File]
var customBamImageFolder = Option.empty[File]
private var enabled: Boolean = true
private var shakeEnabled: Boolean = false
def isHotkeyHeatup: Boolean = hotkeyHeatup
def setHotkeyHeatup(h: Boolean): Unit = {
hotkeyHeatup = h
}
def flameImageFolder: Option[File] = {
if (!_isCustomFlameImages) Some(new File("fire/animated/256"))
else customFlameImageFolder
}
def bamImageFolder: Option[File] = {
if (!_isCustomBamImages) Some(new File("bam")) else customBamImageFolder
}
def getFrameRateHertz(): Int = frameRate.toHertz.toInt
def setFrameRateHertz(f: Int): Unit = {
frameRate = f hertz
}
def increaseHeatup(
dataContext: Option[DataContext] = Option.empty[DataContext],
keyStroke: Option[KeyStroke] = Option.empty[KeyStroke]): Unit = {
val currentTime = System.currentTimeMillis().milliseconds
lastKeys = (keyStroke, currentTime, myCaretListener.caretCount.get()) :: filterLastKeys(
currentTime)
dataContext.foreach(dc =>
maybeElementOfPowerContainerManager.foreach(_.showIndicator(dc)))
}
var previousValues = List.empty[Double]
def reduceHeatup: Unit = {
val ct = System.currentTimeMillis().milliseconds
lastKeys = filterLastKeys(ct)
adjustValueFactor
}
private def adjustValueFactor: Unit = {
if (previousValues.size > frameRate.toHertz) {
previousValues = previousValues.dropRight(1)
}
val unlimited = rawValueFactorUnlimited
val wouldValues = unlimited :: previousValues
val slope = if (wouldValues.size > 1) {
val s = new SimpleRegression()
wouldValues.zipWithIndex.foreach {
case (e, i) =>
s.addData(i, e)
}
s.getSlope
} else {
0
}
val maxSlope = 0.001
val maxSlopeValue = 0.0005
if (slope > maxSlope) {
rvf *= (1.0 + maxSlopeValue)
} else if (slope < -maxSlope) {
rvf *= (1.0 - maxSlopeValue)
} else {
rvf = unlimited
}
previousValues ::= rvf
}
private def filterLastKeys(currentTime: Time): List[HeatupKey] = {
lastKeys.filter(_._2 >= currentTime - heatupTimeMillis)
}
var rvf: Double = 0.0
def rawValueFactor: Double = { rvf }
def rawValueFactorUnlimited: Double = {
val base = heatupFactor +
((1 - heatupFactor) * rawTimeFactorFromKeyStrokes)
(base - heatupThreshold) / (1 - heatupThreshold)
}
def rawTimeFactorFromKeyStrokes: Double = {
val tf = Try {
if (heatupTimeMillis < 1.seconds) {
1 ea
} else {
val MaxKeystrokesOverHeatupTime
: Dimensionless = heatupTimeMillis * keyStrokesPerMinute
val vals: Seq[Dimensionless] = lastKeys.map {
case (Some(ks), _, caretCount) =>
val size = Seq(InputEvent.CTRL_DOWN_MASK,
InputEvent.ALT_DOWN_MASK,
InputEvent.SHIFT_DOWN_MASK)
.count(m => (ks.getModifiers & m) > 0)
(size * hotkeyWeight * caretCount)
case (_, _, caretCount) => 1.ea * caretCount
}
val keystrokesOverHeatupTime: Dimensionless = vals.foldLeft(0.ea)(_ + _)
val res = (keystrokesOverHeatupTime / MaxKeystrokesOverHeatupTime).ea
res
}
}.getOrElse(0 ea)
tf.toEach
}
def valueFactor: Dimensionless = {
math.min(math.max(rawValueFactor, 0), 1) * 100.percent
}
def timeFactor: Dimensionless = {
math.min(math.max(rawTimeFactorFromKeyStrokes, 0), 1) * 100.percent
}
val myCaretListener = new MyCaretListener()
// NOTE: this is called in the constructor, at the very end of this file 👇
def initComponent(): Unit = {
PowerMode.logger.debug(s"initComponent... ${System.out.hashCode()}",
new Exception("let's trace the origin"))
logger.debug("initComponentPrinted...")
val editorFactory = EditorFactory.getInstance
maybeElementOfPowerContainerManager = Some(
new ElementOfPowerContainerManager)
maybeElementOfPowerContainerManager.foreach(
editorFactory.addEditorFactoryListener(_, this))
EditorFactory
.getInstance()
.getEventMulticaster
.addCaretListener(myCaretListener, this)
maybeElementOfPowerContainerManager.map(cm => {
val typedAction = TypedAction
.getInstance()
typedAction
.setupRawHandler(new MyTypedActionHandler(typedAction.getRawHandler))
})
val l = new HotkeyHeatupListener
PowerMode.logger.debug("initComponent done")
}
override def getState: PowerMode = {
this
}
override def loadState(state: PowerMode): Unit = {
XmlSerializerUtil.copyBean(state, this)
}
def isEnabled: Boolean = {
enabled
}
def setEnabled(enabled: Boolean): Unit = {
this.enabled = enabled
}
def isShakeEnabled: Boolean = {
shakeEnabled
}
def setShakeEnabled(shakeEnabled: Boolean): Unit = {
this.shakeEnabled = shakeEnabled
}
def getSparkCount: Int = sparkCount
def setSparkCount(sparkCount: Int): Unit = {
this.sparkCount = sparkCount
}
def getSparkLife: Int = sparkLife
def setSparkLife(sparkRange: Int): Unit = {
this.sparkLife = sparkRange
}
def getShakeRange: Int = shakeRange
def setShakeRange(shakeRange: Int): Unit = {
this.shakeRange = shakeRange
}
def getHeatup: Int = (heatupFactor * 100).toInt
def setHeatup(heatup: Int): Unit = {
this.heatupFactor = heatup / 100.0
}
def getHeatupTime: Int = heatupTimeMillis.toMilliseconds.toInt
def setHeatupTime(heatupTime: Int): Unit = {
this.heatupTimeMillis = math.max(0, heatupTime) milliseconds
}
def getFlameLife: Int = {
maxFlameLife.toMilliseconds.toInt
}
def setFlameLife(flameLife: Int): Unit = {
maxFlameLife = flameLife.milliseconds
}
def getmaxFlameSize: Int = {
maxFlameSize
}
def setmaxFlameSize(maxFlameSize: Int): Unit = {
this.maxFlameSize = maxFlameSize
}
def getKeyStrokesPerMinute: Int = {
(keyStrokesPerMinute * 1.minutes).toEach.toInt
}
def setKeyStrokesPerMinute(keyStrokesPerMinute: Int): Unit = {
this.keyStrokesPerMinute = keyStrokesPerMinute / 1.minutes
}
def isFlamesEnabled: Boolean = {
flamesEnabled
}
def setFlamesEnabled(flamesEnabled: Boolean): Unit = {
this.flamesEnabled = flamesEnabled
}
def isSparksEnabled: Boolean = {
sparksEnabled
}
def setSparksEnabled(sparksEnabled: Boolean): Unit = {
this.sparksEnabled = sparksEnabled
}
def getSparkSize: Int = {
sparkSize
}
def setSparkSize(sparkSize: Int): Unit = {
this.sparkSize = sparkSize
}
def getGravityFactor(): Double = gravityFactor
def setGravityFactor(f: Double): Unit = {
gravityFactor = f
}
def getSparkVelocityFactor(): Double = sparkVelocityFactor
def setSparkVelocityFactor(f: Double): Unit = {
sparkVelocityFactor = f
}
def getRedFrom: Int = {
redFrom
}
def setRedFrom(redFrom: Int): Unit = {
if (redFrom <= redTo)
this.redFrom = redFrom
}
def getRedTo: Int = {
redTo
}
def setRedTo(redTo: Int): Unit = {
if (redTo >= redFrom)
this.redTo = redTo
}
def getGreenTo: Int = {
greenTo
}
def setGreenTo(greenTo: Int): Unit = {
if (greenTo >= greenFrom)
this.greenTo = greenTo
}
def getGreenFrom: Int = {
greenFrom
}
def setGreenFrom(gf: Int): Unit = {
if (gf <= greenTo)
greenFrom = gf
}
def getBlueTo: Int = {
blueTo
}
def setBlueTo(blueTo: Int): Unit = {
if (blueTo >= getBlueFrom)
this.blueTo = blueTo
}
def getBlueFrom: Int = {
blueFrom
}
def setBlueFrom(bf: Int): Unit = {
if (bf <= blueTo)
blueFrom = bf
}
def getColorAlpha: Int = {
colorAlpha
}
def setColorAlpha(alpha: Int): Unit = {
colorAlpha = alpha
}
def getSoundsFolder: String =
soundsFolder.map(_.getAbsolutePath).getOrElse("")
def setSoundsFolder(file: String): Unit = {
soundsFolder = Option(new File(file))
}
def getIsCaretAction: Boolean = {
caretAction
}
def setIsCaretAction(isCaretAction: Boolean): Unit = {
this.caretAction = isCaretAction
}
def getIsSoundsPlaying: Boolean = isSoundsPlaying
def setIsSoundsPlaying(isSoundsPlaying: Boolean): Unit = {
this.isSoundsPlaying = isSoundsPlaying
}
def getBamLife: Double = bamLife.toMilliseconds
def setBamLife(l: Long): Unit = {
bamLife = l.milliseconds
}
def getIsBamEnabled: Boolean = isBamEnabled
def setIsBamEnabled(b: Boolean): Unit = {
isBamEnabled = b
}
def getHeatupThreshold: Int = {
(heatupThreshold * 100.0).toInt
}
def setHeatupThreshold(t: Int): Unit = {
heatupThreshold = t / 100.0
}
def getIsPowerIndicatorEnabled: Boolean = {
powerIndicatorEnabled
}
def setIsPowerIndicatorEnabled(enabled: Boolean): Unit = {
powerIndicatorEnabled = enabled
}
def isCustomFlameImages: Boolean = _isCustomFlameImages
def setCustomFlameImages(s: Boolean): Unit = {
_isCustomFlameImages = s
}
def isCustomBamImages: Boolean = _isCustomBamImages
def setCustomBamImages(s: Boolean): Unit = {
_isCustomBamImages = s
}
def getCustomFlameImageFolder: String =
customFlameImageFolder.map(_.getAbsolutePath).getOrElse("")
def setCustomFlameImageFolder(file: String): Unit = {
customFlameImageFolder = Option(new File(file))
}
def getCustomBamImageFolder: String =
customBamImageFolder.map(_.getAbsolutePath).getOrElse("")
def setCustomBamImageFolder(file: String): Unit = {
customBamImageFolder = Option(new File(file))
}
def getIsSingleBamImagePerEvent(): Boolean = {
isSingleBamImagePerEvent
}
def setIsSingleBamImagePerEvent(isSingleBamImagePerEvent: Boolean): Unit = {
this.isSingleBamImagePerEvent = isSingleBamImagePerEvent
}
}
================================================
FILE: src/main/scala/de/ax/powermode/PowerModeConfigurable.scala
================================================
/*
* Copyright 2015 Baptiste Mesta
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ax.powermode
import com.intellij.openapi.options.ConfigurableBase
import de.ax.powermode.power.ui.PowerModeConfigurableUI
/**
* @author Baptiste Mesta
*/
class PowerModeConfigurable()
extends ConfigurableBase[PowerModeConfigurableUI, PowerMode](
"power.mode.II",
"Power Mode II",
"power.mode.II") {
val settings: PowerMode = PowerMode.getInstance
protected def getSettings: PowerMode = {
if (settings == null) {
throw new IllegalStateException("power mode is null")
}
settings
}
protected def createUi: PowerModeConfigurableUI = {
new PowerModeConfigurableUI(settings)
}
}
================================================
FILE: src/main/scala/de/ax/powermode/PowerModeStartup.scala
================================================
package de.ax.powermode
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import java.util
object PowerModeStartup {
def start(): Unit = synchronized {
PowerMode.logger.debug(s"starting instance on out ${System.out.hashCode()}")
val x = PowerMode.getInstance
PowerMode.logger.debug(s"started instance ${x.hashCode()}")
}
}
class PowerModeStartup extends com.intellij.ide.AppLifecycleListener {
override def appFrameCreated(commandLineArgs: util.List[String]): Unit = {
PowerModeStartup.start()
}
}
================================================
FILE: src/main/scala/de/ax/powermode/TogglePowerMode.scala
================================================
package de.ax.powermode
import com.intellij.openapi.actionSystem.{AnAction, AnActionEvent}
/**
* Created by nyxos on 31.03.16.
*/
class TogglePowerMode extends AnAction with Power {
def actionPerformed(e: AnActionEvent): Unit = {
powerMode.setEnabled(!powerMode.isEnabled)
}
}
================================================
FILE: src/main/scala/de/ax/powermode/Util.scala
================================================
package de.ax.powermode
import com.intellij.openapi.editor.impl.EditorImpl
import com.intellij.openapi.editor.{Caret, Editor, EditorKind, VisualPosition}
import java.awt.Point
import scala.util.Try
/**
* Created by nyxos on 30.09.16.
*/
object Util {
def alpha(f: Float): Float = {
if (f < 0) {
0f
} else if (f > 1) {
1f
} else {
f
}
}
def isActualEditor(editor: Editor): Boolean = {
println(
s"editor.getClass: ${editor.getClass} ${editor.toString} ${editor.getEditorKind} ${editor.getDocument.isWritable}")
editor match {
case e if e.isViewer =>
false
case e if e.isOneLineMode =>
false
case impl: EditorImpl =>
try {
Set(EditorKind.MAIN_EDITOR, EditorKind.DIFF)
.contains(impl.getEditorKind)
} catch {
case _: Throwable => false
}
case _ =>
false
}
}
def editorOk(editor: Editor, maxSize: Int): Boolean = {
!(editor match {
case impl: EditorImpl =>
try {
impl.getPreferredSize.height < maxSize || impl.getPreferredSize.width < maxSize
} catch {
case _: Throwable => true
}
case _ =>
false
})
}
def interceptError[T](f: => T): T = {
try {
f
} catch {
case e: Throwable =>
throw e
}
}
def getPoint(position: VisualPosition, editor: Editor): Point = {
val p: Point = editor.visualPositionToXY(position)
val location = editor.getScrollingModel.getVisibleArea.getLocation
p.translate(-location.x, -location.y)
p
}
def getCaretPosition(caret: Caret): Try[Point] = Try {
getPoint(caret.getVisualPosition, caret.getEditor)
}
def getCaretPosition(editor: Editor, c: Caret): Try[Point] = Try {
val p: Point = editor.visualPositionToXY(c.getVisualPosition)
val location = editor.getScrollingModel.getVisibleArea.getLocation
p.translate(-location.x, -location.y)
p
}
}
================================================
FILE: src/main/scala/de/ax/powermode/cache/Cache.scala
================================================
package de.ax.powermode.cache
import scala.collection.mutable;
class Cache[K, V, I](compareValue: K => I, shoudUpdate: K => Boolean) {
val cache: mutable.HashMap[K, (V, I)] = mutable.HashMap.empty[K, (V, I)]
def getOrUpdate(k: K)(orUpdate: => Option[V]): Option[V] = {
var kInfo = compareValue(k)
val maybeV1 = if (shoudUpdate(k)) {
None
} else {
cache
.get(k)
.collect { case (v, i) => if (kInfo == i) Some(v) else None }
.flatten
}
maybeV1 match {
case Some(v) =>
Some(v)
case None =>
val nvo = orUpdate
nvo.map(nv => cache.put(k, (nv, kInfo)))
nvo
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/package.scala
================================================
/**
* Created by nyxos on 13.03.16.
*/
package object powermode {
type PowerColor = (Float, Float, Float, Float)
}
================================================
FILE: src/main/scala/de/ax/powermode/power/ElementOfPower.scala
================================================
package de.ax.powermode.power
import de.ax.powermode.Power
import java.awt.Graphics
/**
* Created by nyxos on 10.03.16.
*/
trait ElementOfPower extends Power {
def update(delta: Float): Boolean
def render(g: Graphics, dxx: Int, dyy: Int): Unit
def life: Long
def initLife: Long
def lifeFactor: Float = {
1 - ((life - System.currentTimeMillis()) / initLife.toFloat)
}
def alive: Boolean = {
life > System
.currentTimeMillis() && powerMode.isEnabled //&& width >= 0 && width <= _width
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/color/ColorEdges.scala
================================================
package de.ax.powermode.power.color
import java.awt.Color
class ColorEdges(var leftTop: MyPaint = MyPaint(0, 0, 0, 255),
var rightTop: MyPaint = MyPaint(255, 0, 0, 255),
var leftBottom: MyPaint = MyPaint(0, 255, 0, 255),
var rightBottom: MyPaint = MyPaint(255, 255, 0, 255)) {
implicit def mp2Color(mp: MyPaint): Color = mp.color
def getLeftTop: Color = {
return leftTop
}
def getRightTop: Color = {
return rightTop
}
def getLeftBottom: Color = {
return leftBottom
}
def getRightBottom: Color = {
return rightBottom
}
def updateColors(c1: Int): Unit = {
leftTop = updateMyPaint(c1, leftTop)
rightTop = updateMyPaint(c1, rightTop)
leftBottom = updateMyPaint(c1, leftBottom)
rightBottom = updateMyPaint(c1, rightBottom)
}
private def updateMyPaint(c: Int, MyPaint: MyPaint): MyPaint = {
return MyPaint.withBlue(c)
}
def setLeftTop(leftTop: MyPaint): Unit = {
this.leftTop = leftTop
}
def setRightTop(rightTop: MyPaint): Unit = {
this.rightTop = rightTop
}
def setLeftBottom(leftBottom: MyPaint): Unit = {
this.leftBottom = leftBottom
}
def setRightBottom(rightBottom: MyPaint): Unit = {
this.rightBottom = rightBottom
}
def setRedFrom(redFrom: Int): Unit = {
this.leftTop = leftTop.withRed(redFrom)
this.leftBottom = leftBottom.withRed(redFrom)
}
def setRedTo(redTo: Int): Unit = {
this.rightTop = rightTop.withRed(redTo)
this.rightBottom = rightBottom.withRed(redTo)
}
def setGreenFrom(redFrom: Int): Unit = {
this.leftTop = leftTop.withGreen(redFrom)
this.rightTop = rightTop.withGreen(redFrom)
}
def setGreenTo(redTo: Int): Unit = {
this.leftBottom = leftBottom.withGreen(redTo)
this.rightBottom = rightBottom.withGreen(redTo)
}
def setBlueFrom(redFrom: Int): Unit = {
this.leftTop = leftTop.withBlue(redFrom)
this.leftBottom = leftBottom.withBlue(redFrom)
}
def setBlueTo(redTo: Int): Unit = {
this.rightTop = rightTop.withBlue(redTo)
this.rightBottom = rightBottom.withBlue(redTo)
}
def setAlpha(alpha: Int): Unit = {
this.leftTop = leftTop.withAlpha(alpha)
this.leftBottom = leftBottom.withAlpha(alpha)
this.rightTop = rightTop.withAlpha(alpha)
this.rightBottom = rightBottom.withAlpha(alpha)
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/color/MyPaint.scala
================================================
package de.ax.powermode.power.color
import java.awt.Color
/**
* Created by nyxos on 05.07.16.
*/
case class MyPaint(r: Int, g: Int, b: Int, a: Int = 255) {
def withRed(rr: Int) = copy(r = rr)
def withGreen(gg: Int) = copy(g = gg)
def withBlue(bb: Int) = copy(b = bb)
def withAlpha(aa: Int) = copy(a = aa)
lazy val color = new Color(r, g, b, a)
}
================================================
FILE: src/main/scala/de/ax/powermode/power/element/PowerBam.scala
================================================
package de.ax.powermode.power.element
import de.ax.powermode.power.ElementOfPower
import de.ax.powermode.{ImageUtil, Util}
import java.awt.image.BufferedImage
import java.awt.{AlphaComposite, Graphics, Graphics2D}
import scala.language.postfixOps
/**
* Created by nyxos on 28.12.16.
*/
case class PowerBam(_x: Float,
_y: Float,
var _width: Float,
var _height: Float,
initLife: Long)
extends ElementOfPower {
logger.debug(s"x ${_x}, y ${_y}, w ${_width}, h ${_height}")
val life = System.currentTimeMillis() + initLife
var x: Double = _x
var y: Double = _y
var width: Double = 0
var height: Double = 0
var i = 0
var currentImage: Option[BufferedImage] =
if (powerMode.isSingleBamImagePerEvent) {
val bis = ImageUtil.imagesForPath(powerMode.bamImageFolder)
val img = bis((math.random() * (bis.size - 1)) % bis.size toInt)
val ih = img.getWidth / img.getHeight.toDouble
logger.debug(s"img ${img.getWidth} / ${img.getHeight}")
logger.debug(s"old ${_width} ${_height}")
if (ih > 1) {
_height = (_height / ih).toFloat
_width = _width
} else {
_height = _height
_width = (_width * ih).toFloat
}
logger.debug(s"new ${_width} ${_height}")
Option(img)
} else {
None
}
override def update(delta: Float): Boolean = {
if (alive) {
if (!powerMode.isSingleBamImagePerEvent) {
val bis = ImageUtil.imagesForPath(powerMode.bamImageFolder)
if (bis.nonEmpty) {
currentImage = Option(bis(i % bis.size))
}
}
i += 1
x = _x + (0.5 * _width) - (0.5 * _width * lifeFactor)
y = _y + (0.5 * _height) - (0.5 * _height * lifeFactor)
width = _width * lifeFactor
height = _height * lifeFactor
}
!alive
}
override def render(g: Graphics, dxx: Int, dyy: Int): Unit = {
currentImage.foreach { currentImage =>
if (alive) {
val g2d: Graphics2D = g.create.asInstanceOf[Graphics2D]
g2d.setComposite(
AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
Util.alpha(0.9f * (1 - lifeFactor))))
g2d.drawImage(currentImage,
x + dxx toInt,
y + dyy toInt,
width toInt,
height toInt,
null)
g2d.dispose()
}
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/element/PowerFlame.scala
================================================
package de.ax.powermode.power.element
import de.ax.powermode.power.ElementOfPower
import de.ax.powermode.{ImageUtil, Util}
import java.awt.image.BufferedImage
import java.awt.{AlphaComposite, Graphics, Graphics2D}
case class PowerFlame(_x: Int,
_y: Int,
_width: Int,
_height: Int,
initLife: Long,
up: Boolean)
extends ElementOfPower {
val life = System.currentTimeMillis() + initLife
var x = _x
var y = _y
var width = 0
var height = 0
var i = 0
var currentImage: BufferedImage = null
override def update(delta: Float): Boolean = {
if (alive) {
val flameImages1 = ImageUtil.imagesForPath(powerMode.flameImageFolder)
if (flameImages1.nonEmpty) {
currentImage = flameImages1(i % flameImages1.size)
}
i += 1
x = _x - (0.5 * _width * lifeFactor).toInt
if (up)
y = _y - (1.1 * _height * lifeFactor).toInt
else
y = _y + (0.25 * _height * lifeFactor).toInt
width = (_width * lifeFactor).toInt
height = (_height * lifeFactor).toInt
}
!alive
}
override def render(g: Graphics, dxx: Int, dyy: Int): Unit = {
if (alive) {
val g2d: Graphics2D = g.create.asInstanceOf[Graphics2D]
g2d.setComposite(
AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
Util.alpha(0.9f * (1 - lifeFactor))))
if (up) {
if (currentImage != null)
g2d.drawImage(currentImage, x + dxx, y + dyy, width, height, null)
} else {
// flip horizontally
if (currentImage != null)
g2d.drawImage(currentImage,
x + dxx,
y + dyy + height,
width,
-height,
null)
}
g2d.dispose()
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/element/PowerIndicator.scala
================================================
package de.ax.powermode.power.element
import com.intellij.openapi.editor.Editor
import de.ax.powermode.power.ElementOfPower
import de.ax.powermode.{PowerMode, Util}
import squants.DimensionlessConversions.DimensionlessConversions
import java.awt._
import java.awt.image.BufferedImage
import scala.annotation.tailrec
import scala.collection.mutable
import scala.language.postfixOps
import scala.ref.WeakReference
/**
* Created by nyxos on 25.02.17.
*/
object PowerIndicator {
var indicators = mutable.Queue.empty[PowerIndicator]
def addIndicator(i: PowerIndicator): Unit = {
this.synchronized {
PowerIndicator.indicators += i
PowerIndicator.indicators = PowerIndicator.indicators.filter(i => i.alive)
}
}
val grands = Seq("perfect",
"excellent",
"superb",
"sublime",
"dominating",
"marvelous",
"splendid",
"majestic",
"unreal",
"fabulous",
"great")
var lastGrand = Option.empty[String]
def genGrand: String = {
@tailrec
def nextGrand(lastGrand: Option[String]): String = {
val grand = grands((Math.random() * 100023451).toInt % grands.length) + "!"
if (lastGrand.contains(grand)) {
nextGrand(lastGrand)
} else {
grand
}
}
if (math.random * 100.percent < Seq(
1.percent,
0.2 * PowerMode.getInstance.valueFactor).max) {
val grand = nextGrand(lastGrand)
lastGrand = Some(grand)
grand
} else {
""
}
}
}
case class PowerIndicator(_x: Float,
_y: Float,
_width: Float,
_height: Float,
initLife: Long,
editor: WeakReference[Editor])
extends ElementOfPower {
val identifier = System.currentTimeMillis() + (Math.random() * 1000000)
var diffLife = Option.empty[Long]
var x: Double = _x
var y: Double = _y
var width: Double = 0
var height: Double = 0
PowerIndicator.addIndicator(this)
val life2 = System.currentTimeMillis() + initLife
val grand = PowerIndicator.genGrand
override def life = {
if (isLast) {
math.max(life2.toDouble, System.currentTimeMillis() + (initLife * 0.75)) toLong
} else {
diffLife = Some(
diffLife.getOrElse(
System.currentTimeMillis() + (initLife * 0.75) toLong))
diffLife.get
}
}
def isLast: Boolean = {
PowerIndicator.indicators != null && PowerIndicator.indicators.lastOption
.filter(_ != null)
.exists(i => identifier == i.identifier)
}
override def update(delta: Float): Boolean = {
if (alive) {
x = _x + (0.5 * _width) - (0.5 * _width * (1 - lifeFactor))
y = _y + (0.5 * _height) - (0.5 * _height * (1 - lifeFactor))
width = _width * (1 - lifeFactor)
height = _height * (1 - lifeFactor)
}
!alive
}
var lastScrollPosition = Option.empty[(Int, Int)]
override def render(g: Graphics, _dxx: Int, _dyy: Int): Unit = {
def limit(v: Int, max: Int): Int = {
// if (v > max) max else if (v < -max) -max else v
v
}
if (alive && powerMode.isEnabled && powerMode.powerIndicatorEnabled) {
val Some((dxx, dyy)) = lastScrollPosition
.map(lp => {
val (nx, ny) =
(editor.get.get.getScrollingModel.getHorizontalScrollOffset,
editor.get.get.getScrollingModel.getVerticalScrollOffset)
(lp._1 - nx, lp._2 - ny)
})
.orElse(Some(0, 0))
.map { case (x, y) => (limit(x, 100), limit(y, 100)) }
val g2d: Graphics2D = g.create.asInstanceOf[Graphics2D]
g2d.setComposite(
AlphaComposite.getInstance(
AlphaComposite.SRC_OVER,
Util.alpha(1f * (1 - lifeFactor) * (1 - lifeFactor))))
val bufferedImage =
new BufferedImage(600, 600, BufferedImage.TYPE_INT_ARGB)
val graphics = bufferedImage.getGraphics
drawIndicator(graphics.asInstanceOf[Graphics2D],
bufferedImage.getWidth,
bufferedImage.getHeight)
g2d.drawImage(bufferedImage,
math.max(x, 0) - dxx toInt,
math.max(y, 0) - dyy toInt,
width toInt,
height toInt,
null)
g2d.dispose()
lastScrollPosition = Some(
(editor.get.get.getScrollingModel.getHorizontalScrollOffset,
editor.get.get.getScrollingModel.getVerticalScrollOffset))
}
}
private def drawIndicator(graphics: Graphics2D, width: Int, height: Int) = {
graphics.setColor(Color.darkGray)
graphics.fillRect(10, 10, width - 10, 200)
graphics.setColor(Color.white)
graphics.setFont(new Font("Dialog", Font.PLAIN, 100))
graphics.drawString((powerMode.rawValueFactor * 100).toInt.toString + " %",
10,
100)
graphics.setColor(Color.white)
graphics.drawString(grand, 10, 200)
graphics.setColor(Color.white)
var f =
math.min(powerMode.rawValueFactor, 20 + (powerMode.rawValueFactor % 1))
var max: Double = math.ceil(f)
val maxLines = 8
val maxYSpace = 30 * maxLines / max
var barHeight = math.min(50, 0.75 * maxYSpace) toInt
var barSpace = math.min(17, 0.25 * maxYSpace) toInt
while (f > 0) {
graphics.setColor(Color.white)
graphics.fillRect(10,
height - (((max.toInt + 1) - math
.ceil(f)) * (barSpace + barHeight)) toInt,
width * (if (f >= 1) 1 else f) - 10 toInt,
barHeight)
graphics.setColor(Color.black)
graphics.setStroke(new BasicStroke(10))
graphics.drawRect(9,
height - (((max.toInt + 1) - math
.ceil(f)) * (barSpace + barHeight)) - 1 toInt,
width * (if (f >= 1) 1 else f) - 1 - 10 toInt,
barHeight - 1)
f -= 1
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/element/PowerSpark.scala
================================================
/*
* Copyright 2015 Baptiste Mesta
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ax.powermode.power.element
import de.ax.powermode.Util
import de.ax.powermode.power.ElementOfPower
import powermode.PowerColor
import java.awt._
/**
* @author Baptiste Mesta
*/
case class PowerSpark(var x: Float,
var y: Float,
dx: Float,
var dy: Float,
size: Float,
val initLife: Long,
color: PowerColor,
gravityFactor: Float)
extends ElementOfPower {
val life = System.currentTimeMillis() + initLife
def update(delta: Float): Boolean = {
dy += (0.07f * gravityFactor) * delta
x += dx * delta
y += dy * delta
!alive
}
def render(g: Graphics, dxx: Int, dyy: Int): Unit = {
if (alive) {
val g2d: Graphics2D = g.create.asInstanceOf[Graphics2D]
g2d.setColor(
new Color(color._1, color._2, color._3, Util.alpha(color._4)))
g2d.fillOval((dxx + x - (size / 2)).toInt,
(dyy + y - (size / 2)).toInt,
size.toInt,
size.toInt)
g2d.dispose()
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/hotkeys/HotkeyHeatupListener.scala
================================================
package de.ax.powermode.power.hotkeys
import com.intellij.ide.DataManager
import com.intellij.openapi.actionSystem._
import com.intellij.openapi.components.ApplicationComponent
import com.intellij.openapi.keymap.KeymapManager
import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
import com.intellij.util.{SlowOperations, ThrowableRunnable}
import de.ax.powermode.Power
import java.awt._
import java.awt.event._
import java.util
import javax.swing._
class HotkeyHeatupListener extends AWTEventListener with Power {
lazy val allActionKeyStrokes: Set[KeyStroke] =
actionsToKeyStrokes.values.flatten.toSet
lazy val actionsToKeyStrokes = {
Map(
KeymapManager.getInstance.getActiveKeymap.getActionIds.toList
.map(ActionManager.getInstance.getAction)
.filter(a => a != null && a.getShortcutSet != null)
.map { a =>
val keyStrokes = a.getShortcutSet.getShortcuts.toList
.filter(_.isKeyboard)
.filter(_.isInstanceOf[KeyboardShortcut])
.map(_.asInstanceOf[KeyboardShortcut])
.flatMap(a => Seq(a.getFirstKeyStroke, a.getSecondKeyStroke))
(a, keyStrokes)
}: _*)
}
override def eventDispatched(e: AWTEvent): Unit = {
SlowOperations.allowSlowOperations(new ThrowableRunnable[Exception] {
override def run(): Unit = {
if (powerMode.isEnabled && powerMode.isHotkeyHeatup) {
e match {
case event: KeyEvent => {
if ((event.getModifiersEx & (InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)) > 0) {
val eventKeyStroke =
KeyStroke.getKeyStroke(event.getKeyCode, event.getModifiersEx)
val isHotkey = allActionKeyStrokes.contains(eventKeyStroke)
if (isHotkey) {
powerMode.increaseHeatup(
Option(
DataManager
.getInstance()
.getDataContext(event.getComponent)),
Option(eventKeyStroke))
}
}
}
case _ =>
}
}
}
})
}
Toolkit.getDefaultToolkit.addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK)
}
================================================
FILE: src/main/scala/de/ax/powermode/power/management/ElementOfPowerContainer.scala
================================================
/*
* Copyright 2015 Baptiste Mesta
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ax.powermode.power.management
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.event._
import com.intellij.openapi.editor.{Editor, ScrollingModel}
import de.ax.powermode.power.ElementOfPower
import de.ax.powermode.power.element.{
PowerBam,
PowerFlame,
PowerIndicator,
PowerSpark
}
import de.ax.powermode.{Power, Util}
import powermode.PowerColor
import squants.Dimensionless
import squants.DimensionlessConversions.{
DimensionlessConversions,
dimensionlessToDouble,
each
}
import squants.time.Time._
import squants.time.Frequency._
import squants.time.TimeConversions._
import squants.time.FrequencyConversions._
import squants.MetricSystem._
import squants.time.{Frequency, Time, TimeUnit}
import scala.language.postfixOps
import java.awt.event.{ComponentEvent, ComponentListener}
import java.awt.{Graphics, Point, Rectangle}
import java.util.TimerTask
import javax.swing._
import scala.jdk.CollectionConverters._
import scala.language.postfixOps
import scala.ref.WeakReference
object ElementOfPowerContainer {
private val logger = Logger.getInstance(this.getClass)
}
/**
* @author Baptiste Mesta
*/
class ElementOfPowerContainer(editor: WeakReference[Editor])
extends JComponent
with ComponentListener
with Power {
private val _myParent: Option[WeakReference[JComponent]] =
editor.get.map(_.getContentComponent).map(WeakReference.apply)
private def myParent: Option[JComponent] = _myParent.flatMap(_.get)
myParent.foreach { myParent =>
myParent.add(this)
this.setBounds(myParent.getBounds)
setVisible(true)
myParent.addComponentListener(this)
}
val shakeComponents: Seq[JComponent] =
Seq(editor.get.map(_.getComponent), editor.get.map(_.getContentComponent)).flatten
var elementsOfPower: Seq[(ElementOfPower, (Int, Int))] = Seq.empty
var lastShake: Long = System.currentTimeMillis()
var shakeData: Option[(Int, Int, Int, Int)] = Option.empty
var lastUpdate: Long = System.currentTimeMillis()
var lastPositions: Seq[(Point, Point)] = Seq.empty
editor.get.foreach(_.getCaretModel.addCaretListener(new CaretListener {
def changeCarets: Unit = {
lastPositions = {
editor.get.toSeq.flatMap(editor =>
editor.getCaretModel.getAllCarets.asScala.toSeq.map(caret => {
(Util.getPoint(
editor.offsetToVisualPosition(caret.getSelectionStart),
caret.getEditor),
Util.getPoint(editor.offsetToVisualPosition(caret.getSelectionEnd),
caret.getEditor))
}))
}
}
override def caretPositionChanged(e: CaretEvent): Unit = changeCarets
override def caretAdded(e: CaretEvent): Unit = changeCarets
override def caretRemoved(e: CaretEvent): Unit = changeCarets
}))
private val self: ElementOfPowerContainer = this
editor.get.foreach(_.getDocument.addDocumentListener(new DocumentListener {
val wr: WeakReference[ElementOfPowerContainer] = WeakReference(self)
override def documentChanged(documentEvent: DocumentEvent): Unit = {
wr.get match {
case Some(elementOfPowerContainer) =>
elementOfPowerContainer.initializeCaretBam(documentEvent)
case None => documentEvent.getDocument.removeDocumentListener(this)
}
}
}))
private def initializeCaretBam(e: DocumentEvent) = {
if (powerMode.isBamEnabled) {
editor.get.foreach { editor =>
val shouldAnimate = {
e.getNewFragment.length() > 100 ||
e.getOldFragment.length() > 100 ||
e.getOldFragment.toString.count('\n' == _) > 1 ||
e.getNewFragment.toString.count('\n' == _) > 1
}
if (shouldAnimate) {
val width = {
val l = (e.getOldFragment.toString + e.getNewFragment.toString)
.split("\n")
if (l.nonEmpty) {
(l.maxBy(_.length).length / 2.0) * editor.getLineHeight
} else {
0
}
}
val timer = new java.util.Timer()
timer.schedule(new TimerTask {
override def run(): Unit = {
SwingUtilities.invokeLater(() => {
performCaretBam(width)
})
}
}, 300)
}
}
}
}
private def performCaretBam(width: Double) = {
if (lastPositions.nonEmpty) {
editor.get.foreach { editor =>
val myLastPositions = lastPositions
SwingUtilities.invokeLater(() => {
myLastPositions.foreach {
case (a, b) =>
val w = editor.getComponent.getWidth.toDouble
val h = editor.getComponent.getHeight.toDouble
initializeAnimation(a,
new Point(math.min(0, b.x), math.min(0, b.y)),
width)
}
})
}
}
}
def getAllCaretPositions: Seq[Point] = {
editor.get.toList.flatMap { editor =>
editor.getCaretModel.getAllCarets.asScala.toSeq.map(caret =>
Util.getPoint(caret.getVisualPosition, caret.getEditor))
}
}
def updateElementsOfPower(): Unit = {
var delta = (System.currentTimeMillis() - lastUpdate)
val value = (1000.0 / powerMode.frameRate.toHertz)
if (delta > value * 2) {
//smoothen movement when frame rate drops
delta = 16
}
lastUpdate = System.currentTimeMillis()
val db: Double = 1000.0 / 16
if (elementsOfPower.nonEmpty) {
elementsOfPower =
elementsOfPower.filterNot(p => p._1.update((delta / db).toFloat))
repaint()
}
}
def addPowerIndicator(): Unit = {
val indicatorWidth = 100
elementsOfPower :+= (PowerIndicator(
(getMyBounds.width - 20 - indicatorWidth).toFloat,
(getMyBounds.height - 20 - indicatorWidth).toFloat,
indicatorWidth.toFloat,
indicatorWidth.toFloat,
1000L,
editor
), getScrollPosition)
}
def initializeAnimation(point: Point): Unit = {
this.setBounds(getMyBounds)
if (powerMode.isSparksEnabled) {
addSparks(point)
}
if (powerMode.isFlamesEnabled) {
addFlames(point)
}
if (powerMode.isShakeEnabled) {
doShake(shakeComponents)
}
repaint()
}
def initializeAnimation(a: Point, b: Point, lineWidth: Double): Unit = {
val x = a.x
var y = a.y
val width = math.max(b.x - x, 50)
val height = math.max(b.y - y, 50)
val dim =
(Seq(
Seq(lineWidth.toInt, width, height, 50).max,
editor.get.get.getComponent.getWidth,
editor.get.get.getComponent.getHeight).min * powerMode.valueFactor).toInt
if (b.y - y.abs < dim) {
y = y - dim / 2
}
elementsOfPower :+= (PowerBam(
math.max(0, x).toFloat,
math.max(0, y).toFloat,
dim.toFloat,
dim.toFloat,
(powerMode.bamLife * powerMode.valueFactor).toMilliseconds.toLong), getScrollPosition)
}
def addFlames(point: Point): Unit = {
val base = 0.3
val wh = (powerMode.maxFlameSize * base +
((math.random * powerMode.maxFlameSize * (1 - base)) * powerMode.valueFactor)).toInt
val initLife =
(powerMode.maxFlameLife * powerMode.valueFactor).toMilliseconds.toInt
if (initLife > 100) {
elementsOfPower :+= (PowerFlame(point.x + 5,
point.y - 1,
wh,
wh,
initLife,
true), getScrollPosition)
elementsOfPower :+= (PowerFlame(point.x + 5,
point.y + 15,
wh,
wh,
initLife,
false), getScrollPosition)
}
}
def addSparks(point: Point): Unit = {
for (i <- 0 to (powerMode.sparkCount * powerMode.valueFactor).toInt) {
addSpark(point.x, point.y)
}
}
def addSpark(x: Int, y: Int): Unit = {
val dx
: Double = (Math.random * 2) * (if (Math.random > 0.5) -1 else 1) * powerMode.sparkVelocityFactor
val dy: Double = ((Math.random * -3) - 1) * powerMode.sparkVelocityFactor
val size = ((Math.random * powerMode.sparkSize) + 1).toInt
val life = Math.random() * powerMode.getSparkLife * powerMode.valueFactor
val powerSpark = PowerSpark(x.toFloat,
y.toFloat,
dx.toFloat,
dy.toFloat,
size.toFloat,
life.toLong,
genNextColor,
powerMode.gravityFactor.toFloat)
elementsOfPower :+= (powerSpark, getScrollPosition)
}
def genNextColor: PowerColor =
(getColorPart(powerMode.getRedFrom, powerMode.getRedTo),
getColorPart(powerMode.getGreenFrom, powerMode.getGreenTo),
getColorPart(powerMode.getBlueFrom, powerMode.getBlueTo),
powerMode.getColorAlpha / 255f)
def getColorPart(from: Int, to: Int): Float = {
(((Math.random() * (to - from)) + from) / 255).toFloat
}
def getScrollPosition = (
editor.get.get.getScrollingModel.getHorizontalScrollOffset,
editor.get.get.getScrollingModel.getVerticalScrollOffset
)
def doShake(myShakeComponents: Seq[JComponent]): Unit = {
if (Util.editorOk(editor.get.get, 100)) {
shakeData = shakeData match {
case Some((dx, dy, scrollX, scrollY)) =>
myShakeComponents.foreach { myShakeComponent =>
val bounds: Rectangle = myShakeComponent.getBounds
myShakeComponent.setBounds(bounds.x + dx,
bounds.y + dy,
bounds.width,
bounds.height)
}
None
case None =>
val dx = generateShakeOffset
val dy = generateShakeOffset
val scrollX =
editor.get.get.getScrollingModel.getHorizontalScrollOffset
val scrollY = editor.get.get.getScrollingModel.getVerticalScrollOffset
myShakeComponents.foreach { myShakeComponent =>
val bounds: Rectangle = myShakeComponent.getBounds
myShakeComponent.setBounds(bounds.x + dx,
bounds.y + dy,
bounds.width,
bounds.height)
}
Some((-dx, -dy, scrollX, scrollY))
}
lastShake = System.currentTimeMillis()
}
}
def generateShakeOffset: Int = {
val range = powerMode.shakeRange * powerMode.valueFactor
(range - (Math.random * 2 * range)).toInt
}
def componentResized(e: ComponentEvent): Unit = {
setBounds(getMyBounds)
logger.trace("Resized")
}
def componentMoved(e: ComponentEvent): Unit = {
setBounds(getMyBounds)
logger.trace("Moved")
}
def getMyBounds: Rectangle = {
val area = editor.get.get.getScrollingModel.getVisibleArea
val rectangle = new Rectangle(area.x, area.y, area.width, area.height)
rectangle
}
def componentShown(e: ComponentEvent): Unit = {}
def componentHidden(e: ComponentEvent): Unit = {}
protected override def paintComponent(g: Graphics): Unit = {
super.paintComponent(g)
if (powerMode.isEnabled) {
if (shakeData.isDefined &&
System.currentTimeMillis() - lastShake > 100 &&
shakeData.get._1.abs < 50 && shakeData.get._2.abs < 50) {
doShake(Seq(editor.get.get.getComponent))
}
renderElementsOfPower(g)
}
}
def renderElementsOfPower(g: Graphics): Unit = {
val scrollingModel: ScrollingModel = editor.get.get.getScrollingModel
val xyNew = (
scrollingModel.getHorizontalScrollOffset,
scrollingModel.getVerticalScrollOffset
)
elementsOfPower.foreach { pp =>
val (elementOfPower, (x, y)) = pp
val dxx = x - xyNew._1
val dyy = y - xyNew._2
elementOfPower.render(g, dxx, dyy)
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/management/ElementOfPowerContainerManager.scala
================================================
/*
* Copyright 2015 Baptiste Mesta
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ax.powermode.power.management
import com.intellij.openapi.actionSystem.{
CommonDataKeys,
DataConstants,
DataContext,
PlatformCoreDataKeys,
PlatformDataKeys
}
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.event.{
EditorFactoryEvent,
EditorFactoryListener
}
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import de.ax.powermode.power.sound.PowerSound
import de.ax.powermode.{Power, PowerMode, Util}
import java.awt._
import javax.swing._
import scala.collection.mutable
import scala.language.postfixOps
import scala.util.{Failure, Success, Try}
import com.ryanstull.nullsafe._
import scala.ref.WeakReference
/**
* @author Baptiste Mesta
*/
class ElementOfPowerContainerManager extends EditorFactoryListener with Power {
def ForceTry[X](f: => X): Try[X] = {
try {
Success(f).filter(_ != null)
} catch {
case e: Throwable =>
e.printStackTrace()
logger.debug("info doing ForceTry", e)
Failure(e)
}
}
/**
* editor.hashcode -> elementOfPowerContainer
*/
private val elementsOfPowerContainers
: mutable.WeakHashMap[Editor, WeakReference[ElementOfPowerContainer]] =
mutable.WeakHashMap.empty
private lazy val triedSound: Try[PowerSound] =
powerMode.mediaPlayerExists.flatMap { _ =>
Try {
new PowerSound(powerMode.soundsFolder,
powerMode.valueFactor,
(powerMode.minVolume, powerMode.maxVolume))
}
}
lazy val sound = synchronized { triedSound }
def showIndicator(dataContext: DataContext): Unit = {
if (powerMode.powerIndicatorEnabled && powerMode.isEnabled) {
val maybeProject: Seq[Project] = Seq(ForceTry {
dataContext.getData(CommonDataKeys.PROJECT)
}, ForceTry {
dataContext.getData(CommonDataKeys.PROJECT)
}).to(LazyList).flatMap { o =>
o.toOption.flatMap(Option(_)).map(_.asInstanceOf[Project])
}
maybeProject.headOption.foreach(p => {
val textEditor: Editor =
FileEditorManager.getInstance(p).getSelectedTextEditor
SwingUtilities.invokeLater(() => {
opt(textEditor)
.flatMap(elementsOfPowerContainers.get)
.flatMap(_.get)
.foreach(_.addPowerIndicator())
})
})
}
}
val elementsOfPowerUpdateThread = new Thread(new Runnable() {
def run: Unit = {
while (true) {
try {
if (powerMode != null) {
powerMode.reduceHeatup
updateSound
updateContainers
try {
Thread.sleep((1000 / powerMode.frameRate.toHertz) toInt)
} catch {
case ignored: InterruptedException => {}
}
}
} catch {
case e: Throwable => PowerMode.logger.debug(e.getMessage, e)
}
}
}
def updateContainers: Unit = {
elementsOfPowerContainers.values
.flatMap(_.get)
.foreach(_.updateElementsOfPower())
}
var soundErrorLogged = System.currentTimeMillis()
def updateSound: Unit = {
try {
if (powerMode.isEnabled &&
powerMode.soundsFolder.exists(f => f.exists() && f.isDirectory)
&& powerMode.isSoundsPlaying) {
if (sound.isFailure && soundErrorLogged + 5000 < System
.currentTimeMillis()) {
logger.debug(sound.failed.get.getMessage, sound.failed.get)
soundErrorLogged += 1
}
sound.foreach(_.play())
} else {
sound.foreach(_.stop())
}
sound.foreach(_.setVolume(powerMode.valueFactor))
} catch {
case e: Throwable =>
logger.debug(e.getMessage, e)
}
}
})
elementsOfPowerUpdateThread.start()
override def editorCreated(event: EditorFactoryEvent): Unit = {
val editor: Editor = event.getEditor
val isActualEditor = Try {
Util.isActualEditor(editor)
}.getOrElse(false)
if (isActualEditor) {
opt(editor)
.flatMap(
editor =>
elementsOfPowerContainers
.put(editor,
WeakReference(
new ElementOfPowerContainer(WeakReference(editor)))))
}
}
override def editorReleased(event: EditorFactoryEvent): Unit = {
opt(event.getEditor)
.flatMap(elementsOfPowerContainers.remove)
}
def initializeAnimation(editor: Editor, pos: Point): Unit = {
if (powerMode.isEnabled) {
SwingUtilities.invokeLater(new Runnable() {
def run: Unit = {
initializeInUI(editor, pos)
}
})
}
}
private def initializeInUI(editor: Editor, pos: Point): Unit = {
opt(editor)
.flatMap(elementsOfPowerContainers.get)
.flatMap(_.get)
.foreach(_.initializeAnimation(pos))
}
def dispose: Unit = {
elementsOfPowerUpdateThread.interrupt()
elementsOfPowerContainers.clear()
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/sound/MediaPlayer.scala
================================================
package de.ax.powermode.power.sound
import com.intellij.openapi.diagnostic.Logger
import de.ax.powermode.PowerMode
import javazoom.jl.player.{FactoryRegistry, HackyJavaSoundAudioDevice}
import javazoom.jl.player.advanced.{
AdvancedPlayer,
PlaybackEvent,
PlaybackListener
}
import squants.Dimensionless
import squants.DimensionlessConversions.dimensionlessToDouble
import scala.util.{Try, Using}
import java.io.{
BufferedInputStream,
File,
FileInputStream,
FileOutputStream,
InputStream
}
import javax.sound.sampled.{Control, FloatControl}
object MediaPlayer {
def getSoundAudioDevice =
new HackyJavaSoundAudioDevice
}
class MediaPlayer(file: File, volumeRange: => (Dimensionless, Dimensionless))
extends AutoCloseable {
def logger: Logger = PowerMode.logger
val stream: BufferedInputStream = new BufferedInputStream(
new FileInputStream(file))
val soundAudioDevice: HackyJavaSoundAudioDevice =
MediaPlayer.getSoundAudioDevice
val player: AdvancedPlayer = new AdvancedPlayer(stream, soundAudioDevice)
val listener: PlaybackListener = new PlaybackListener {
override def playbackStarted(evt: PlaybackEvent): Unit = {
logger.debug("playbackStarted")
}
override def playbackFinished(evt: PlaybackEvent): Unit = {
notifyHandlers()
}
}
var playThread = Option.empty[Thread]
player.setPlayBackListener(listener)
def setVolume(rawGain: Dimensionless): Unit = {
logger.trace("setting volume ")
val control: Option[FloatControl] = Option(soundAudioDevice.source)
.flatMap(sourceDataLine =>
Try {
sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN)
}.toOption)
.map(_.asInstanceOf[FloatControl])
control.foreach { volControl =>
val range: (Dimensionless, Dimensionless) = volumeRange
val volRange = volControl
.getMaximum() - volControl.getMinimum()
val gain: Dimensionless =
if (range._2 < rawGain) {
range._2
} else if (rawGain < range._1) {
range._1
} else {
rawGain
}
// use log scale to have a smoother transition at higer volume level
// slight higher volume changes are much harsher compared to the same amount of low volume change otherwise.
val logGain: Double = math.log10(1 + gain.toDouble * 9)
val newGain: Double =
Math.min(Math.max(volControl.getMinimum() + (logGain * volRange),
volControl.getMinimum()),
volControl.getMaximum() * 0.99999999)
logger.trace(
s"setting volume ${rawGain}factor. was limited to ${gain} applied to ${volControl
.getMinimum()} - ${volControl.getMaximum()} => ${newGain}")
logger.trace("Was: " + volControl.getValue() + " Will be: " + newGain);
volControl.setValue(newGain.toFloat);
}
}
def play(): Unit = {
if (playThread.isEmpty) {
logger.debug("starting")
playThread = Option(new Thread(new Runnable() {
override def run(): Unit = {
try {
player.play()
} catch {
case e: Throwable =>
logger.debug("playback info", e)
notifyHandlers()
throw e
} finally {
playThread = None
}
}
}))
playThread.foreach(_.start())
logger.debug("started")
}
}
private def notifyHandlers(): Unit = {
handlers.foreach(_.apply())
}
def stop(): Unit = {
if (player != null) {
try {
logger.debug("stopping")
player.stop()
logger.debug("stopped")
} catch {
case e: Exception =>
logger.debug("info stopping", e)
}
}
}
var handlers = List.empty[() => Unit]
def onError(fn: () => Unit): Unit = {
handlers ::= fn
}
def close(): Unit = {
try {
stop()
} finally {
player.close()
stream.close()
}
}
}
================================================
FILE: src/main/scala/de/ax/powermode/power/sound/PowerSound.scala
================================================
package de.ax.powermode.power.sound
import de.ax.powermode.Power
import javazoom.jl.player.{FactoryRegistry, HackyJavaSoundAudioDevice, Player}
import squants.Dimensionless
import java.io.File
import scala.language.postfixOps
/**
* Created by nyxos on 03.10.16.
*/
class PowerSound(folder: => Option[File],
valueFactor: => Dimensionless,
volumeRange: => (Dimensionless, Dimensionless))
extends Power {
def next(): Unit = {
this.synchronized {
doStop()
doPlay()
}
}
def resetPlaying(): Unit = {
playing = false
}
def files: Array[File] =
folder
.flatMap(f => Option(f.listFiles()))
.getOrElse(Array.empty[File])
.filter(f => f.isFile && f.exists)
var playing = false
var current = 1
def setVolume(v: Dimensionless): Unit = this.synchronized {
mediaPlayer.foreach(_.setVolume(v))
}
private def doStop(): Unit = {
this.synchronized {
mediaPlayer.foreach(_.stop())
while (mediaPlayer.exists(_.playThread.exists(_.isAlive))) {
logger.debug("still playing in thread")
Thread.sleep(100)
}
mediaPlayer = Option.empty
playing = false
}
}
var mediaPlayer = Option.empty[MediaPlayer]
var index = 0
var lastFolder: Option[File] = folder
def stop(): Unit = this.synchronized {
doStop()
}
def play(): Unit = this.synchronized {
if (lastFolder.map(_.getAbsolutePath) != folder.map(_.getAbsolutePath)) {
doStop()
}
doPlay()
}
private def doPlay(): Unit = {
val myFiles: Array[File] = files
if (!playing && myFiles != null && !myFiles.isEmpty) {
index = (Math.random() * (200 * myFiles.length)).toInt % myFiles.length
val f = myFiles(index)
logger.debug(
s"${this.hashCode()} playing sound file '$f' playing=${playing} mediaPlayer=${mediaPlayer}")
try {
playing = true
mediaPlayer = Some {
val mediaPlayer =
new de.ax.powermode.power.sound.MediaPlayer(f, volumeRange)
mediaPlayer.onError(() => {
logger.debug("resetting")
resetPlaying()
})
mediaPlayer.setVolume(valueFactor)
mediaPlayer.play()
mediaPlayer
}
logger.debug(s"started playing '$f'")
} catch {
case e: Throwable =>
e.printStackTrace()
playing = false
}
}
}
}