The-Powder-Toy/src/interface/Textbox.cpp
2012-08-09 20:38:06 +01:00

651 lines
14 KiB
C++

#include <string>
#include <iostream>
#include <stdexcept>
#include <time.h>
#include "Config.h"
#include "interface/Point.h"
#include "interface/Textbox.h"
#include "interface/Keys.h"
#include "ContextMenu.h"
using namespace ui;
Textbox::Textbox(Point position, Point size, std::string textboxText, std::string textboxPlaceholder):
Label(position, size, ""),
actionCallback(NULL),
masked(false),
border(true),
mouseDown(false),
limit(std::string::npos),
inputType(All),
keyDown(0),
characterDown(0)
{
placeHolder = textboxPlaceholder;
SetText(textboxText);
cursor = text.length();
menu->RemoveItem(0);
menu->AddItem(ContextMenuItem("Cut", 1, true));
menu->AddItem(ContextMenuItem("Copy", 0, true));
menu->AddItem(ContextMenuItem("Paste", 2, true));
}
Textbox::~Textbox()
{
if(actionCallback)
delete actionCallback;
}
void Textbox::SetHidden(bool hidden)
{
menu->RemoveItem(0);
menu->RemoveItem(1);
menu->RemoveItem(2);
menu->AddItem(ContextMenuItem("Cut", 1, !hidden));
menu->AddItem(ContextMenuItem("Copy", 0, !hidden));
menu->AddItem(ContextMenuItem("Paste", 2, true));
masked = hidden;
}
void Textbox::SetPlaceholder(std::string text)
{
placeHolder = text;
}
void Textbox::SetText(std::string newText)
{
backingText = newText;
if(masked)
{
std::string maskedText = std::string(newText);
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
Label::SetText(maskedText);
}
else
Label::SetText(newText);
cursor = newText.length();
if(cursor)
{
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
}
else
{
cursorPositionY = cursorPositionX = 0;
}
}
Textbox::ValidInput Textbox::GetInputType()
{
return inputType;
}
void Textbox::SetInputType(ValidInput input)
{
inputType = input;
}
void Textbox::SetLimit(size_t limit)
{
this->limit = limit;
}
size_t Textbox::GetLimit()
{
return limit;
}
std::string Textbox::GetText()
{
return backingText;
}
void Textbox::OnContextMenuAction(int item)
{
switch(item)
{
case 0:
copySelection();
break;
case 1:
cutSelection();
break;
case 2:
pasteIntoSelection();
break;
}
}
void Textbox::cutSelection()
{
char * clipboardText;
clipboardText = clipboard_pull_text();
std::string newText = std::string(clipboardText);
free(clipboardText);
if(HasSelection())
{
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
return;
clipboard_push_text((char*)backingText.substr(getLowerSelectionBound(), getHigherSelectionBound()-getLowerSelectionBound()).c_str());
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
cursor = getLowerSelectionBound();
}
else
{
clipboard_push_text((char*)backingText.c_str());
backingText.clear();
}
ClearSelection();
if(masked)
{
std::string maskedText = std::string(backingText);
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
Label::SetText(maskedText);
}
else
{
text = backingText;
}
if(actionCallback)
actionCallback->TextChangedCallback(this);
if(multiline)
updateMultiline();
updateSelection();
TextPosition(text);
if(cursor)
{
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
}
else
{
cursorPositionY = cursorPositionX = 0;
}
}
void Textbox::selectAll()
{
selectionIndex0 = 0;
selectionIndex1 = text.length();
updateSelection();
}
void Textbox::pasteIntoSelection()
{
char * clipboardText;
clipboardText = clipboard_pull_text();
std::string newText = std::string(clipboardText);
free(clipboardText);
if(HasSelection())
{
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
return;
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
cursor = getLowerSelectionBound();
}
backingText.insert(cursor, newText);
cursor = cursor+newText.length();
ClearSelection();
if(masked)
{
std::string maskedText = std::string(backingText);
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
Label::SetText(maskedText);
}
else
{
text = backingText;
}
if(actionCallback)
actionCallback->TextChangedCallback(this);
if(multiline)
updateMultiline();
updateSelection();
TextPosition(text);
if(cursor)
{
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
}
else
{
cursorPositionY = cursorPositionX = 0;
}
}
bool Textbox::CharacterValid(Uint16 character)
{
switch(inputType)
{
case Number:
case Numeric:
return (character >= '0' && character <= '9');
case All:
default:
return (character >= ' ' && character < 127);
}
return false;
}
void Textbox::Tick(float dt)
{
Label::Tick(dt);
if((keyDown || characterDown) && repeatTime <= clock())
{
OnVKeyPress(keyDown, characterDown, false, false, false);
repeatTime = clock()+(0.03 * CLOCKS_PER_SEC);
}
}
void Textbox::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt)
{
keyDown = 0;
characterDown = 0;
}
void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
{
characterDown = character;
keyDown = key;
repeatTime = clock()+(0.3 * CLOCKS_PER_SEC);
OnVKeyPress(key, character, shift, ctrl, alt);
}
void Textbox::OnVKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
{
bool changed = false;
if(ctrl && key == 'c' && !masked)
{
copySelection();
return;
}
if(ctrl && key == 'v')
{
pasteIntoSelection();
return;
}
if(ctrl && key == 'x' && !masked)
{
cutSelection();
return;
}
if(ctrl && key == 'a')
{
selectAll();
return;
}
try
{
switch(key)
{
case KEY_HOME:
cursor = 0;
ClearSelection();
break;
case KEY_END:
cursor = backingText.length();
ClearSelection();
break;
case KEY_LEFT:
if(cursor > 0)
cursor--;
ClearSelection();
break;
case KEY_RIGHT:
if(cursor < backingText.length())
cursor++;
ClearSelection();
break;
case KEY_DELETE:
if(HasSelection())
{
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
return;
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
cursor = getLowerSelectionBound();
changed = true;
}
else if(backingText.length() && cursor < backingText.length())
{
if(ctrl)
backingText.erase(cursor, backingText.length()-cursor);
else
backingText.erase(cursor, 1);
changed = true;
}
ClearSelection();
break;
case KEY_BACKSPACE:
if(HasSelection())
{
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
return;
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
cursor = getLowerSelectionBound();
changed = true;
}
else if(backingText.length() && cursor > 0)
{
if(ctrl)
{
backingText.erase(0, cursor);
cursor = 0;
}
else
{
backingText.erase(cursor-1, 1);
cursor--;
}
changed = true;
}
ClearSelection();
break;
}
if(CharacterValid(character))
{
if(HasSelection())
{
if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length())
return;
backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound());
cursor = getLowerSelectionBound();
}
if(limit==std::string::npos || backingText.length() < limit)
{
if(cursor == backingText.length())
{
backingText += character;
}
else
{
backingText.insert(cursor, 1, (char)character);
}
}
cursor++;
changed = true;
ClearSelection();
}
}
catch(std::out_of_range &e)
{
cursor = 0;
backingText = "";
}
if(inputType == Number)
{
//Remove extra preceding 0's
while(backingText[0] == '0' && backingText.length()>1)
backingText.erase(backingText.begin());
//If there is no content, replace with 0
if(!backingText.length())
backingText = "0";
}
if(cursor > backingText.length())
cursor = backingText.length();
if(changed)
{
if(masked)
{
std::string maskedText = std::string(backingText);
std::fill(maskedText.begin(), maskedText.end(), '\x8D');
Label::SetText(maskedText);
}
else
{
text = backingText;
}
if(actionCallback)
actionCallback->TextChangedCallback(this);
}
if(multiline)
updateMultiline();
updateSelection();
TextPosition(text);
if(cursor)
{
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
}
else
{
cursorPositionY = cursorPositionX = 0;
}
}
void Textbox::OnMouseClick(int x, int y, unsigned button)
{
if(button != BUTTON_RIGHT)
{
mouseDown = true;
cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), x-textPosition.X, y-textPosition.Y);
if(cursor)
{
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
}
else
{
cursorPositionY = cursorPositionX = 0;
}
}
Label::OnMouseClick(x, y, button);
}
void Textbox::OnMouseUp(int x, int y, unsigned button)
{
mouseDown = false;
Label::OnMouseUp(x, y, button);
}
void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy)
{
if(mouseDown)
{
cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), localx-textPosition.X, localy-textPosition.Y);
if(cursor)
{
Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY);
}
else
{
cursorPositionY = cursorPositionX = 0;
}
}
Label::OnMouseMoved(localx, localy, dx, dy);
}
void Textbox::Draw(const Point& screenPos)
{
Label::Draw(screenPos);
Graphics * g = Engine::Ref().g;
if(IsFocused())
{
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
g->draw_line(screenPos.X+textPosition.X+cursorPositionX, screenPos.Y-2+textPosition.Y+cursorPositionY, screenPos.X+textPosition.X+cursorPositionX, screenPos.Y+9+textPosition.Y+cursorPositionY, 255, 255, 255, 255);
}
else
{
if(!text.length())
{
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, placeHolder, textColour.Red, textColour.Green, textColour.Blue, 170);
}
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255);
}
if(Appearance.icon)
g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon);
}
/*
Textbox::Textbox(Point position, Point size, std::string textboxText):
Component(position, size),
text(textboxText),
actionCallback(NULL),
masked(false),
border(true)
{
SetText(textboxText);
cursor = text.length();
}
Textbox::~Textbox()
{
if(actionCallback)
delete actionCallback;
}
void Textbox::TextPosition()
{
if(cursor)
{
cursorPosition = Graphics::textnwidth((char *)displayText.c_str(), cursor);
}
else
{
cursorPosition = 0;
}
Component::TextPosition(displayText);
}
void Textbox::SetText(std::string text)
{
cursor = text.length();
this->text = text;
this->displayText = text;
TextPosition();
}
void Textbox::SetDisplayText(std::string text)
{
displayText = text;
TextPosition();
}
std::string Textbox::GetText()
{
return text;
}
void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
{
bool changed = false;
try
{
switch(key)
{
case KEY_HOME:
cursor = 0;
break;
case KEY_END:
cursor = text.length();
break;
case KEY_LEFT:
if(cursor > 0)
cursor--;
break;
case KEY_RIGHT:
if(cursor < text.length())
cursor++;
break;
case KEY_DELETE:
if(text.length() && cursor < text.length())
{
if(ctrl)
text.erase(cursor, text.length()-cursor);
else
text.erase(cursor, 1);
changed = true;
}
break;
case KEY_BACKSPACE:
if(text.length() && cursor > 0)
{
if(ctrl)
{
text.erase(0, cursor);
cursor = 0;
}
else
{
text.erase(cursor-1, 1);
cursor--;
}
changed = true;
}
break;
}
if(character >= ' ' && character < 127)
{
if(cursor == text.length())
{
text += character;
}
else
{
text.insert(cursor, 1, (char)character);
}
cursor++;
changed = true;
}
}
catch(std::out_of_range &e)
{
cursor = 0;
text = "";
}
if(changed)
{
if(masked)
{
char * tempText = new char[text.length()+1];
std::fill(tempText, tempText+text.length(), 0x8d);
tempText[text.length()] = 0;
displayText = std::string(tempText);
delete tempText;
}
else
{
displayText = text;
}
if(actionCallback)
actionCallback->TextChangedCallback(this);
}
TextPosition();
}
void Textbox::Draw(const Point& screenPos)
{
if(!drawn)
{
TextPosition();
drawn = true;
}
Graphics * g = Engine::Ref().g;
if(IsFocused())
{
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255);
g->draw_line(screenPos.X+textPosition.X+cursorPosition, screenPos.Y+3, screenPos.X+textPosition.X+cursorPosition, screenPos.Y+12, 255, 255, 255, XRES+BARSIZE);
}
else
{
if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255);
}
g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, 255, 255, 255, 255);
if(Appearance.icon)
g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon);
}*/