// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdmisc.h"
#ifdef NL_USE_GTK
#include
#include
//#include
#include
#ifdef NL_OS_WINDOWS
// automatically add gtk library
#pragma comment(lib, "gtk-1.3.lib")
#pragma comment(lib, "gdk-1.3.lib")
#pragma comment(lib, "glib-1.3.lib")
#pragma comment(lib, "gthread-1.3.lib")
#endif
#include "nel/misc/path.h"
#include "nel/misc/command.h"
#include "nel/misc/thread.h"
#include "nel/misc/gtk_displayer.h"
using namespace std;
namespace NLMISC {
//
// Variables
//
static vector CommandHistory;
static uint32 CommandHistoryPos = 0;
static CLog *Log = 0;
static GtkWidget *RootWindow = NULL, *OutputText = NULL, *InputText = NULL;
static GtkWidget *hrootbox = NULL, *scrolled_win2 = NULL;
//
// Functions
//
CGtkDisplayer::~CGtkDisplayer ()
{
if (_Init)
{
}
}
gint ButtonClicked(GtkWidget *Widget, gpointer *Data)
{
CGtkDisplayer *disp = (CGtkDisplayer *) Data;
// find the button and execute the command
CSynchronized >::CAccessor access (&(disp->_Labels));
for (uint i = 0; i < access.value().size(); i++)
{
if (access.value()[i].Hwnd == Widget)
{
if(access.value()[i].Value == "@Clear|CLEAR")
{
// special commands because the clear must be called by the display thread and not main thread
disp->clear ();
}
else
{
// the button was found, add the command in the command stack
CSynchronized >::CAccessor accessCommands (&disp->_CommandsToExecute);
string str;
nlassert (!access.value()[i].Value.empty());
nlassert (access.value()[i].Value[0] == '@');
string::size_type pos = access.value()[i].Value.find ("|");
if (pos != string::npos)
{
str = access.value()[i].Value.substr(pos+1);
}
else
{
str = access.value()[i].Value.substr(1);
}
if (!str.empty())
accessCommands.value().push_back(str);
}
break;
}
}
return TRUE;
}
void CGtkDisplayer::updateLabels ()
{
{
CSynchronized >::CAccessor access (&_Labels);
for (uint i = 0; i < access.value().size(); i++)
{
if (access.value()[i].NeedUpdate && !access.value()[i].Value.empty())
{
string n;
if (access.value()[i].Value[0] != '@')
n = access.value()[i].Value;
else
{
string::size_type pos = access.value()[i].Value.find ('|');
if (pos != string::npos)
{
n = access.value()[i].Value.substr (1, pos - 1);
}
else
{
n = access.value()[i].Value.substr (1);
}
}
if (access.value()[i].Hwnd == NULL)
{
// create a button for command and label for variables
if (access.value()[i].Value[0] == '@')
{
access.value()[i].Hwnd = gtk_button_new_with_label (n.c_str());
nlassert (access.value()[i].Hwnd != NULL);
gtk_signal_connect (GTK_OBJECT (access.value()[i].Hwnd), "clicked", GTK_SIGNAL_FUNC (ButtonClicked), (gpointer) this);
GtkLabel *label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(access.value()[i].Hwnd)));
gtk_label_set_justify (label, GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap (label, FALSE);
gtk_widget_show (GTK_WIDGET (access.value()[i].Hwnd));
gtk_box_pack_start (GTK_BOX (hrootbox), GTK_WIDGET (access.value()[i].Hwnd), TRUE, TRUE, 0);
}
else
{
access.value()[i].Hwnd = gtk_label_new ("");
gtk_label_set_justify (GTK_LABEL (access.value()[i].Hwnd), GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap (GTK_LABEL (access.value()[i].Hwnd), FALSE);
gtk_widget_show (GTK_WIDGET (access.value()[i].Hwnd));
gtk_box_pack_start (GTK_BOX (hrootbox), GTK_WIDGET (access.value()[i].Hwnd), TRUE, TRUE, 0);
}
}
if (access.value()[i].Value[0] != '@')
gtk_label_set_text (GTK_LABEL (access.value()[i].Hwnd), n.c_str());
access.value()[i].NeedUpdate = false;
}
}
}
}
// windows delete event => quit
gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
gtk_main_quit();
exit(1);
return FALSE;
}
gint KeyIn(GtkWidget *Widget, GdkEventKey *Event, gpointer *Data)
{
switch (Event->keyval)
{
case GDK_Escape :
gtk_entry_set_text (GTK_ENTRY(Widget), "");
break;
case GDK_Up :
if (CommandHistoryPos > 0) {
CommandHistoryPos--;
gtk_entry_set_text (GTK_ENTRY(Widget), CommandHistory[CommandHistoryPos].c_str());
}
break;
case GDK_Down :
if (CommandHistoryPos + 1 < CommandHistory.size()) {
CommandHistoryPos++;
gtk_entry_set_text (GTK_ENTRY(Widget), CommandHistory[CommandHistoryPos].c_str());
}
break;
case GDK_KP_Enter :
gtk_signal_emit_by_name(GTK_OBJECT(Widget),"activate");
default :
return FALSE;
}
gtk_signal_emit_stop_by_name(GTK_OBJECT(Widget),"key_press_event");
return TRUE;
}
void updateInput ()
{
gtk_widget_grab_focus (InputText);
}
gint KeyOut(GtkWidget *Widget, GdkEventKey *Event, gpointer *Data)
{
updateInput();
gtk_signal_emit_stop_by_name(GTK_OBJECT(Widget),"key_press_event");
return TRUE;
}
/*gint ButtonClear(GtkWidget *Widget, GdkEventKey *Event, gpointer *Data)
{
CGtkDisplayer *disp = (CGtkDisplayer *) Data;
disp->clear ();
return TRUE;
}
*/
// the user typed command, execute it
gint cbValidateCommand (GtkWidget *widget, GdkEvent *event, gpointer data)
{
string cmd = gtk_entry_get_text (GTK_ENTRY(widget));
CommandHistory.push_back (cmd);
// execute the command
if(Log == NULL)
Log = InfoLog;
ICommand::execute (cmd, *Log);
// clear the input text
gtk_entry_set_text (GTK_ENTRY(widget), "");
CommandHistoryPos = CommandHistory.size();
return TRUE;
}
void CGtkDisplayer::setTitleBar (const string &titleBar)
{
string wn;
if (!titleBar.empty())
{
wn += titleBar;
wn += ": ";
}
wn += "Nel Service Console (compiled " __DATE__ " " __TIME__ " in " + nlMode + " mode)";
//nlassert (RootWindow != NULL);
gtk_window_set_title (GTK_WINDOW (RootWindow), wn.c_str());
}
void CGtkDisplayer::open (std::string titleBar, bool iconified, sint x, sint y, sint w, sint h, sint hs, sint fs, const std::string &fn, bool ww, CLog *log)
{
_HistorySize = hs;
if (w == -1)
w = 700;
if (h == -1)
h = 300;
if (hs == -1)
hs = 10000;
gtk_init (NULL, NULL);
Log = log;
// Root window
RootWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (RootWindow), w, h);
gtk_signal_connect (GTK_OBJECT (RootWindow), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL);
// Vertical box
GtkWidget *vrootbox = gtk_vbox_new (FALSE, 0);
nlassert (vrootbox != NULL);
gtk_container_add (GTK_CONTAINER (RootWindow), vrootbox);
// Horizontal box (for labels)
hrootbox = gtk_hbox_new (FALSE, 0);
nlassert (hrootbox != NULL);
gtk_box_pack_start (GTK_BOX (vrootbox), hrootbox, FALSE, FALSE, 0);
/* // Clear button
GtkWidget *button = gtk_button_new_with_label ("Clear");
nlassert (button != NULL);
gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (ButtonClear), (gpointer) this);
gtk_box_pack_start (GTK_BOX (hrootbox), button, FALSE, FALSE, 0);
*/
// Output text
scrolled_win2 = gtk_scrolled_window_new (NULL, NULL);
nlassert (scrolled_win2 != NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win2), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_widget_show (scrolled_win2);
gtk_container_add (GTK_CONTAINER (vrootbox), scrolled_win2);
OutputText = gtk_text_view_new();
nlassert (OutputText != NULL);
PangoFontDescription *fontDesc = pango_font_description_from_string("Monospace 10");
gtk_widget_modify_font(OutputText, fontDesc);
pango_font_description_free(fontDesc);
GtkTextBuffer *textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(OutputText));
GtkTextIter endIter;
gtk_text_buffer_get_end_iter(textBuffer, &endIter);
gtk_text_buffer_create_mark(textBuffer, "endmark", &endIter, false);
gtk_signal_connect(GTK_OBJECT(OutputText),"key_press_event",GTK_SIGNAL_FUNC(KeyOut),NULL);
gtk_text_view_set_editable (GTK_TEXT_VIEW(OutputText), FALSE);
gtk_container_add (GTK_CONTAINER (scrolled_win2), OutputText);
// Input text
InputText = gtk_entry_new ();
nlassert (InputText != NULL);
gtk_signal_connect (GTK_OBJECT(InputText), "activate", GTK_SIGNAL_FUNC(cbValidateCommand), NULL);
gtk_signal_connect(GTK_OBJECT(InputText),"key_press_event",GTK_SIGNAL_FUNC(KeyIn),NULL);
gtk_box_pack_start (GTK_BOX (vrootbox), InputText, FALSE, FALSE, 0);
// gtk_widget_show (button);
gtk_widget_show (OutputText);
gtk_widget_show (InputText);
gtk_widget_show (hrootbox);
gtk_widget_show (vrootbox);
gtk_widget_show (RootWindow);
setTitleBar (titleBar);
_Init = true;
}
void CGtkDisplayer::clear ()
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
// who is taking care of the iterators?
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(OutputText));
gtk_text_buffer_get_bounds(buffer, &start, &end);
gtk_text_buffer_delete(buffer, &start, &end);
}
gint updateInterf (gpointer data)
{
CGtkDisplayer *disp = (CGtkDisplayer *)data;
//
// Update labels
//
disp->updateLabels ();
//
// Display the bufferized string
//
GtkAdjustment *Adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolled_win2));
bool Bottom = (Adj->value >= Adj->upper - Adj->page_size - Adj->step_increment);
bool textChanged = false;
GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(OutputText));
GtkTextIter end_iter;
gtk_text_buffer_get_end_iter(buffer, &end_iter);
std::list >::iterator it;
{
CSynchronized > >::CAccessor access (&disp->_Buffer);
for (it = access.value().begin(); it != access.value().end(); it++)
{
uint32 col = (*it).first;
GtkTextTag *tag = NULL;
if ((col>>24) == 0) {
GdkColor color;
color.red = (col >> 8) & 0xFF00;
color.green = col & 0xFF00;
color.blue = (col << 8) & 0xFF00;
tag = gtk_text_buffer_create_tag(buffer, NULL, "foreground-gdk", &color, "foreground-set", TRUE, NULL);
}
gtk_text_buffer_insert_with_tags(buffer, &end_iter, (*it).second.c_str(), -1, tag, NULL);
textChanged = true;
}
access.value().clear ();
}
if (Bottom && textChanged)
{
GtkTextMark *mark = gtk_text_buffer_get_mark(buffer, "endmark");
gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(OutputText), mark, 0.0, true, 0.0, 1.0);
}
return TRUE;
}
void CGtkDisplayer::display_main ()
{
//
// Manage windows message
//
gtk_timeout_add (10, updateInterf, this);
gtk_main ();
}
void CGtkDisplayer::getWindowPos (uint32 &x, uint32 &y, uint32 &w, uint32 &h)
{
// todo
x = y = w = h = 0;
}
} // NLMISC
#else // NL_USE_GTK
// remove stupid VC6 warnings
void foo_gtk_displayer_cpp() {}
#endif // NL_USE_GTK