ac8e6dcce7
* BUG: The scripter compiles correctly again. [GB.GTK] * BUG: Fix the behaviour of ComboBox, so that it behaves the same way as in gb.qt. * BUG: TextBox.Insert() correctly deletes the selected text before inserting the new one. git-svn-id: svn://localhost/gambas/trunk@2396 867c0c6c-44f3-4631-809d-bfa615b0a4ec
552 lines
11 KiB
C++
552 lines
11 KiB
C++
/***************************************************************************
|
|
|
|
gcombobox.cpp
|
|
|
|
(c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "widgets.h"
|
|
#include "widgets_private.h"
|
|
#include "gmainwindow.h"
|
|
#include "gcombobox.h"
|
|
|
|
/**************************************************************************
|
|
|
|
gComboBox
|
|
|
|
**************************************************************************/
|
|
|
|
static void cb_click(GtkComboBox *widget,gComboBox *data)
|
|
{
|
|
if (data->locked())
|
|
return;
|
|
|
|
if (!data->isReadOnly() && data->count())
|
|
{
|
|
int index = data->index();
|
|
if (index >= 0)
|
|
{
|
|
const char *text = index >= 0 ? data->itemText(index) : NULL;
|
|
if (!text) text = "";
|
|
data->lock();
|
|
gtk_entry_set_text(GTK_ENTRY(data->entry), text);
|
|
data->setIndex(index);
|
|
data->unlock();
|
|
data->emit(SIGNAL(data->onChange));
|
|
}
|
|
}
|
|
|
|
if (!data->_no_click && data->index() >= 0)
|
|
data->emit(SIGNAL(data->onClick));
|
|
}
|
|
|
|
/*static int pathToIndex(GtkTreePath *path)
|
|
{
|
|
gint *indices;
|
|
|
|
if (!path)
|
|
return -1;
|
|
|
|
indices = gtk_tree_path_get_indices(path);
|
|
return indices[0];
|
|
}*/
|
|
|
|
static GtkTreePath *indexToPath(int index)
|
|
{
|
|
char buffer[16];
|
|
sprintf(buffer, "%d", index);
|
|
return gtk_tree_path_new_from_string(buffer);
|
|
}
|
|
|
|
static void combo_cell_text(GtkComboBox *combo, GtkCellRenderer *cell, GtkTreeModel *md, GtkTreeIter *iter, gTree *tr)
|
|
{
|
|
gTreeRow *row = NULL;
|
|
gTreeCell *data;
|
|
const char *buf = "";
|
|
char *key;
|
|
|
|
key = tr->iterToKey(iter);
|
|
if (key)
|
|
row = (gTreeRow*)g_hash_table_lookup(tr->datakey, (gpointer)key);
|
|
|
|
if (row)
|
|
{
|
|
data = row->get(0);
|
|
if (data)
|
|
{
|
|
if (data->text())
|
|
buf = data->text();
|
|
}
|
|
}
|
|
|
|
g_object_set(G_OBJECT(cell),
|
|
"text", buf,
|
|
(void *)NULL);
|
|
}
|
|
|
|
static GtkWidget *_button;
|
|
|
|
static void cb_find_button(GtkWidget *widget, gpointer data)
|
|
{
|
|
if (GTK_IS_TOGGLE_BUTTON(widget))
|
|
_button = widget;
|
|
else if (GTK_IS_CONTAINER(widget))
|
|
gtk_container_forall(GTK_CONTAINER(widget), cb_find_button, NULL);
|
|
}
|
|
|
|
static GtkWidget *find_button(GtkWidget *combo)
|
|
{
|
|
_button = NULL;
|
|
gtk_container_forall(GTK_CONTAINER(combo), cb_find_button, NULL);
|
|
return _button;
|
|
}
|
|
|
|
char *gComboBox::indexToKey(int index)
|
|
{
|
|
char *key;
|
|
GtkTreePath *path = indexToPath(index);
|
|
key = find(path);
|
|
gtk_tree_path_free(path);
|
|
return key;
|
|
}
|
|
|
|
void gComboBox::create(bool readOnly)
|
|
{
|
|
bool first = !border;
|
|
int ind = -1;
|
|
|
|
if (first)
|
|
border = gtk_event_box_new();
|
|
else
|
|
ind = index();
|
|
|
|
if (widget)
|
|
{
|
|
if (cell) g_object_unref(cell);
|
|
cell = NULL;
|
|
gtk_widget_destroy(widget);
|
|
}
|
|
|
|
if (readOnly)
|
|
{
|
|
widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(tree->store));
|
|
entry = NULL;
|
|
|
|
cell = gtk_cell_renderer_text_new ();
|
|
g_object_ref_sink(cell);
|
|
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), cell, true);
|
|
//gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), cell, "text", 0, (void *)NULL);
|
|
g_object_set(cell, "ypad", 0, (void *)NULL);
|
|
gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(widget), cell, (GtkCellLayoutDataFunc)combo_cell_text, (gpointer)tree, NULL);
|
|
}
|
|
else
|
|
{
|
|
GList *cells;
|
|
|
|
widget = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(tree->store), 0);
|
|
entry = gtk_bin_get_child(GTK_BIN(widget));
|
|
|
|
g_signal_handler_disconnect(widget, g_signal_handler_find(widget, G_SIGNAL_MATCH_ID, g_signal_lookup("changed", G_OBJECT_TYPE(widget)), 0, 0, 0, 0));
|
|
|
|
cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(widget));
|
|
cell = (GtkCellRenderer *)cells->data;
|
|
g_list_free(cells);
|
|
g_object_ref(cell);
|
|
//gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), cell, "text", 0, (void *)NULL);
|
|
g_object_set(cell, "ypad", 0, (void *)NULL);
|
|
gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(widget), cell, (GtkCellLayoutDataFunc)combo_cell_text, (gpointer)tree, NULL);
|
|
}
|
|
|
|
if (first)
|
|
{
|
|
realize(false);
|
|
}
|
|
else
|
|
{
|
|
gtk_container_add(GTK_CONTAINER(border), widget);
|
|
gtk_widget_show(widget);
|
|
widgetSignals();
|
|
}
|
|
|
|
g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(cb_click), (gpointer)this);
|
|
|
|
if (entry)
|
|
{
|
|
initEntry();
|
|
//g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(gcb_keypress), (gpointer)this);
|
|
//g_signal_connect(G_OBJECT(entry), "key-release-event", G_CALLBACK(gcb_keyrelease), (gpointer)this);
|
|
g_signal_connect(G_OBJECT(entry), "focus-in-event", G_CALLBACK(gcb_focus_in), (gpointer)this);
|
|
g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(gcb_focus_out), (gpointer)this);
|
|
}
|
|
|
|
updateFocusHandler();
|
|
setBackground(background());
|
|
setForeground(foreground());
|
|
setFont(font());
|
|
|
|
if (ind >= 0)
|
|
setIndex(ind);
|
|
}
|
|
|
|
gComboBox::gComboBox(gContainer *parent) : gTextBox(parent, true)
|
|
{
|
|
/*if (!_style_init)
|
|
{
|
|
gtk_rc_parse_string(
|
|
"style \"gambas-default-combo-box-style\" {\n"
|
|
" GtkComboBox::appears-as-list = 1\n"
|
|
"}\n"
|
|
"class \"GtkComboBox\" style : gtk \"gambas-default-combo-box-style\"\n"
|
|
);
|
|
_style_init = TRUE;
|
|
}*/
|
|
|
|
onChange = NULL;
|
|
onClick = NULL;
|
|
onActivate = NULL;
|
|
|
|
_no_click = false;
|
|
_last_key = 0;
|
|
_model_dirty = false;
|
|
sort = false;
|
|
border = widget = NULL;
|
|
entry = NULL;
|
|
_button = NULL;
|
|
cell = NULL;
|
|
|
|
g_typ = Type_gComboBox;
|
|
|
|
tree = new gTree(NULL);
|
|
tree->addColumn();
|
|
//tree->addColumn();
|
|
tree->setHeaders(false);
|
|
|
|
create(false);
|
|
}
|
|
|
|
gComboBox::~gComboBox()
|
|
{
|
|
if (cell) g_object_unref(cell);
|
|
delete tree;
|
|
//g_object_unref(G_OBJECT(tree->store));
|
|
}
|
|
|
|
void gComboBox::popup()
|
|
{
|
|
gtk_combo_box_popup(GTK_COMBO_BOX(widget));
|
|
}
|
|
|
|
void gComboBox::setRealBackground(gColor color)
|
|
{
|
|
gControl::setRealBackground(color);
|
|
if (entry)
|
|
set_gdk_base_color(entry, color);
|
|
}
|
|
|
|
|
|
void gComboBox::setRealForeground(gColor color)
|
|
{
|
|
gControl::setRealForeground(color);
|
|
if (entry)
|
|
set_gdk_text_color(entry, color);
|
|
}
|
|
|
|
int gComboBox::count()
|
|
{
|
|
return tree->rowCount();
|
|
}
|
|
|
|
int gComboBox::index()
|
|
{
|
|
updateModel();
|
|
return gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
|
|
}
|
|
|
|
char* gComboBox::itemText(int ind)
|
|
{
|
|
gTreeRow *row;
|
|
gTreeCell *cell;
|
|
|
|
updateModel();
|
|
|
|
char *key = indexToKey(ind);
|
|
|
|
if (!key) return NULL;
|
|
row = tree->getRow(key);
|
|
if (!row) return NULL;
|
|
cell = row->get(0);
|
|
if (!cell) return NULL;
|
|
return cell->text();
|
|
}
|
|
|
|
int gComboBox::length()
|
|
{
|
|
gchar *buf;
|
|
|
|
if (!entry)
|
|
{
|
|
buf = itemText(index());
|
|
if (!buf)
|
|
return 0;
|
|
else
|
|
return g_utf8_strlen(buf, -1);
|
|
}
|
|
else
|
|
return gTextBox::length();
|
|
}
|
|
|
|
bool gComboBox::isSorted()
|
|
{
|
|
return tree->isSorted();
|
|
}
|
|
|
|
char* gComboBox::text()
|
|
{
|
|
if (entry)
|
|
return gTextBox::text();
|
|
else
|
|
return itemText(index());
|
|
}
|
|
|
|
void gComboBox::setIndex(int vl)
|
|
{
|
|
if (vl < 0)
|
|
vl = -1;
|
|
else if (vl >= count())
|
|
return;
|
|
|
|
updateModel();
|
|
gtk_combo_box_set_active(GTK_COMBO_BOX(widget), vl);
|
|
}
|
|
|
|
void gComboBox::checkIndex()
|
|
{
|
|
if (index() < 0)
|
|
{
|
|
lock();
|
|
setIndex(0);
|
|
unlock();
|
|
}
|
|
}
|
|
|
|
void gComboBox::setItemText(int ind, const char *text)
|
|
{
|
|
gTreeRow *row;
|
|
gTreeCell *cell;
|
|
|
|
char *key = indexToKey(ind);
|
|
|
|
if (!key) return;
|
|
row = tree->getRow(key);
|
|
if (!row) return;
|
|
cell = row->get(0);
|
|
if (!cell) return;
|
|
|
|
cell->setText(text);
|
|
|
|
updateSort();
|
|
}
|
|
|
|
void gComboBox::setReadOnly(bool vl)
|
|
{
|
|
if (isReadOnly() == vl)
|
|
return;
|
|
create(!isReadOnly());
|
|
}
|
|
|
|
void gComboBox::setSorted(bool vl)
|
|
{
|
|
tree->setSorted(vl);
|
|
}
|
|
|
|
void gComboBox::setText(const char *vl)
|
|
{
|
|
int index = find(vl);
|
|
|
|
if (index >= 0)
|
|
setIndex(index);
|
|
else if (entry)
|
|
gTextBox::setText(vl);
|
|
}
|
|
|
|
static gboolean combo_set_model_and_sort(gComboBox *combo)
|
|
{
|
|
gtk_combo_box_set_model(GTK_COMBO_BOX(combo->widget), GTK_TREE_MODEL(combo->tree->store));
|
|
if (combo->isSorted())
|
|
combo->tree->sort();
|
|
combo->_model_dirty = false;
|
|
if (combo->isReadOnly())
|
|
combo->checkIndex();
|
|
return FALSE;
|
|
}
|
|
|
|
void gComboBox::updateModel()
|
|
{
|
|
if (_model_dirty)
|
|
combo_set_model_and_sort(this);
|
|
}
|
|
|
|
void gComboBox::updateSort()
|
|
{
|
|
if (_model_dirty)
|
|
return;
|
|
|
|
_model_dirty = true;
|
|
gtk_combo_box_set_model(GTK_COMBO_BOX(widget), NULL);
|
|
g_timeout_add(0, (GSourceFunc)combo_set_model_and_sort, this);
|
|
}
|
|
|
|
void gComboBox::add(const char *text, int pos)
|
|
{
|
|
//GtkTreeModel *model;
|
|
gTreeRow *row;
|
|
gTreeCell *cell;
|
|
|
|
char key[16];
|
|
char *after;
|
|
|
|
_last_key++;
|
|
sprintf(key, "%d", _last_key);
|
|
|
|
if (pos < 0 || pos > count())
|
|
after = NULL;
|
|
else
|
|
after = indexToKey(pos);
|
|
|
|
//g_signal_lookup("rowGTK_TYPE_COMBO_BOX);
|
|
|
|
row = tree->addRow(key, NULL, after);
|
|
if (row)
|
|
{
|
|
cell = row->get(0);
|
|
if (cell)
|
|
{
|
|
cell->setText(text);
|
|
updateSort();
|
|
}
|
|
}
|
|
|
|
//gtk_combo_box_set_model(GTK_COMBO_BOX(widget), model);
|
|
}
|
|
|
|
void gComboBox::clear()
|
|
{
|
|
lock();
|
|
tree->clear();
|
|
unlock();
|
|
}
|
|
|
|
int gComboBox::find(const char *text)
|
|
{
|
|
int i;
|
|
const char *it;
|
|
|
|
if (!text)
|
|
text = "";
|
|
|
|
for (i = 0; i < count(); i++)
|
|
{
|
|
it = itemText(i);
|
|
if (!it)
|
|
it = "";
|
|
if (!strcmp(it, text))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void gComboBox::remove(int pos)
|
|
{
|
|
updateModel();
|
|
tree->removeRow(indexToKey(pos));
|
|
updateSort();
|
|
}
|
|
|
|
void gComboBox::resize(int w, int h)
|
|
{
|
|
gControl::resize(w,h);
|
|
|
|
if (entry)
|
|
gtk_widget_set_size_request(entry, width(), height());
|
|
}
|
|
|
|
void gComboBox::setFont(gFont *f)
|
|
{
|
|
gControl::setFont(f);
|
|
if (cell) g_object_set(G_OBJECT(cell), "font-desc", font() ? font()->desc() : NULL, (void *)NULL);
|
|
//if (entry)
|
|
// gtk_widget_modify_font(entry, font() ? font()->desc() : NULL);
|
|
}
|
|
|
|
void gComboBox::setFocus()
|
|
{
|
|
gControl::setFocus();
|
|
if (entry && window()->isVisible())
|
|
gtk_widget_grab_focus(entry);
|
|
}
|
|
|
|
int gComboBox::minimumHeight()
|
|
{
|
|
GtkRequisition req;
|
|
|
|
gtk_widget_size_request(widget, &req);
|
|
if (entry)
|
|
return req.height - 4;
|
|
else
|
|
return req.height;
|
|
}
|
|
|
|
|
|
bool gComboBox::isReadOnly()
|
|
{
|
|
return entry == NULL;
|
|
}
|
|
|
|
|
|
static gboolean button_focus_in(GtkWidget *widget, GdkEventFocus *event, gComboBox *control)
|
|
{
|
|
if (control->isReadOnly())
|
|
return gcb_focus_in(widget, event, control);
|
|
|
|
control->setFocus();
|
|
return false;
|
|
}
|
|
|
|
static gboolean button_focus_out(GtkWidget *widget, GdkEventFocus *event, gComboBox *control)
|
|
{
|
|
if (control->isReadOnly())
|
|
return gcb_focus_out(widget, event, control);
|
|
|
|
return false;
|
|
}
|
|
|
|
void gComboBox::updateFocusHandler()
|
|
{
|
|
GtkWidget *button = find_button(widget);
|
|
|
|
if (button == _button)
|
|
return;
|
|
|
|
_button = button;
|
|
//g_signal_connect(G_OBJECT(button), "key-press-event", G_CALLBACK(gcb_keypress), (gpointer)this);
|
|
//g_signal_connect(G_OBJECT(button), "key-release-event", G_CALLBACK(gcb_keyrelease), (gpointer)this);
|
|
g_signal_connect(G_OBJECT(button), "focus-in-event", G_CALLBACK(button_focus_in), (gpointer)this);
|
|
g_signal_connect(G_OBJECT(button), "focus-out-event", G_CALLBACK(button_focus_out), (gpointer)this);
|
|
}
|