grim/libgnt
Clone
Summary
Browse
Changes
Graph
Merge with release-2.x.y.
2019-05-19, Elliott Sales de Andrade
2b331e084d56
Merge with release-2.x.y.
/*
* GNT - The GLib Ncurses Toolkit
*
* GNT is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This library 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 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 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include
<ctype.h>
#include
<string.h>
#include
"gntinternal.h"
#include
"gntbox.h"
#include
"gntentry.h"
#include
"gntstyle.h"
#include
"gnttree.h"
#include
"gntutils.h"
#include
"gntwidgetprivate.h"
enum
{
SIG_TEXT_CHANGED
,
SIG_COMPLETION
,
SIGS
,
};
typedef
enum
{
ENTRY_JAIL
=
-1
,
/* Suspend the kill ring. */
ENTRY_DEL_BWD_WORD
=
1
,
ENTRY_DEL_BWD_CHAR
,
ENTRY_DEL_FWD_WORD
,
ENTRY_DEL_FWD_CHAR
,
ENTRY_DEL_EOL
,
ENTRY_DEL_BOL
,
}
GntEntryAction
;
typedef
struct
{
GString
*
buffer
;
GntEntryAction
last
;
}
GntEntryKillRing
;
typedef
struct
{
char
*
needle
;
}
GntEntrySearch
;
typedef
struct
{
GntEntryFlag
flag
;
char
*
start
;
char
*
end
;
char
*
scroll
;
/* Current scrolling position */
char
*
cursor
;
/* Cursor location */
/* 0 <= cursor - scroll < widget-width */
size_t
buffer
;
/* Size of the buffer */
int
max
;
/* 0 means infinite */
gboolean
masked
;
GList
*
history
;
/* History of the strings. User can use this by pressing
ctrl+up/down */
int
histlength
;
/* How long can the history be? */
GList
*
suggests
;
/* List of suggestions */
gboolean
word
;
/* Are the suggestions for only a word, or for the whole
thing? */
gboolean
always
;
/* Should the list of suggestions show at all times, or
only on tab-press? */
GntWidget
*
ddown
;
/* The dropdown with the suggested list */
GntEntryKillRing
*
killring
;
GntEntrySearch
*
search
;
}
GntEntryPrivate
;
static
guint
signals
[
SIGS
]
=
{
0
};
static
gboolean
gnt_entry_key_pressed
(
GntWidget
*
widget
,
const
char
*
text
);
static
void
gnt_entry_set_text_internal
(
GntEntry
*
entry
,
const
char
*
text
);
G_DEFINE_TYPE_WITH_PRIVATE
(
GntEntry
,
gnt_entry
,
GNT_TYPE_WIDGET
)
static
gboolean
update_kill_ring
(
GntEntryPrivate
*
priv
,
GntEntryAction
action
,
const
char
*
text
,
int
len
)
{
if
(
action
<
0
)
{
priv
->
killring
->
last
=
action
;
return
FALSE
;
}
if
(
len
==
0
)
len
=
strlen
(
text
);
else
if
(
len
<
0
)
{
text
+=
len
;
len
=
-
len
;
}
if
(
action
!=
priv
->
killring
->
last
)
{
struct
{
GntEntryAction
one
;
GntEntryAction
two
;
}
merges
[]
=
{
{
ENTRY_DEL_BWD_WORD
,
ENTRY_DEL_FWD_WORD
},
{
ENTRY_DEL_BWD_CHAR
,
ENTRY_DEL_FWD_CHAR
},
{
ENTRY_DEL_BOL
,
ENTRY_DEL_EOL
},
{
ENTRY_JAIL
,
ENTRY_JAIL
},
};
int
i
;
for
(
i
=
0
;
merges
[
i
].
one
!=
ENTRY_JAIL
;
i
++
)
{
if
(
merges
[
i
].
one
==
priv
->
killring
->
last
&&
merges
[
i
].
two
==
action
)
{
g_string_append_len
(
priv
->
killring
->
buffer
,
text
,
len
);
break
;
}
else
if
(
merges
[
i
].
one
==
action
&&
merges
[
i
].
two
==
priv
->
killring
->
last
)
{
g_string_prepend_len
(
priv
->
killring
->
buffer
,
text
,
len
);
break
;
}
}
if
(
merges
[
i
].
one
==
ENTRY_JAIL
)
{
g_string_assign
(
priv
->
killring
->
buffer
,
text
);
g_string_truncate
(
priv
->
killring
->
buffer
,
len
);
}
priv
->
killring
->
last
=
action
;
}
else
{
if
(
action
==
ENTRY_DEL_BWD_CHAR
||
action
==
ENTRY_DEL_BWD_WORD
)
g_string_prepend_len
(
priv
->
killring
->
buffer
,
text
,
len
);
else
g_string_append_len
(
priv
->
killring
->
buffer
,
text
,
len
);
}
return
TRUE
;
}
static
void
destroy_suggest
(
GntEntryPrivate
*
priv
)
{
if
(
priv
->
ddown
)
{
gnt_widget_destroy
(
gnt_widget_get_parent
(
priv
->
ddown
));
priv
->
ddown
=
NULL
;
}
}
static
char
*
get_beginning_of_word
(
GntEntryPrivate
*
priv
)
{
char
*
s
=
priv
->
cursor
;
while
(
s
>
priv
->
start
)
{
char
*
t
=
g_utf8_find_prev_char
(
priv
->
start
,
s
);
if
(
isspace
(
*
t
))
break
;
s
=
t
;
}
return
s
;
}
static
gboolean
complete_suggest
(
GntEntry
*
entry
,
const
char
*
text
)
{
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
int
offstart
=
0
,
offend
=
0
;
if
(
priv
->
word
)
{
char
*
s
=
get_beginning_of_word
(
priv
);
const
char
*
iter
=
text
;
offstart
=
g_utf8_pointer_to_offset
(
priv
->
start
,
s
);
while
(
*
iter
&&
toupper
(
*
s
)
==
toupper
(
*
iter
))
{
*
s
++
=
*
iter
++
;
}
if
(
*
iter
)
{
gnt_entry_key_pressed
(
GNT_WIDGET
(
entry
),
iter
);
}
offend
=
g_utf8_pointer_to_offset
(
priv
->
start
,
priv
->
cursor
);
}
else
{
offstart
=
0
;
gnt_entry_set_text_internal
(
entry
,
text
);
offend
=
g_utf8_strlen
(
text
,
-1
);
}
g_signal_emit
(
G_OBJECT
(
entry
),
signals
[
SIG_COMPLETION
],
0
,
priv
->
start
+
offstart
,
priv
->
start
+
offend
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
return
TRUE
;
}
static
int
max_common_prefix
(
const
char
*
s
,
const
char
*
t
)
{
const
char
*
f
=
s
;
while
(
*
f
&&
*
t
&&
*
f
==
*
t
++
)
f
++
;
return
f
-
s
;
}
static
gboolean
show_suggest_dropdown
(
GntEntry
*
entry
)
{
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
char
*
suggest
=
NULL
;
gsize
len
;
int
offset
=
0
,
x
,
y
;
int
count
=
0
;
GList
*
iter
;
const
char
*
text
=
NULL
;
const
char
*
sgst
=
NULL
;
int
max
=
-1
;
if
(
priv
->
word
)
{
char
*
s
=
get_beginning_of_word
(
priv
);
suggest
=
g_strndup
(
s
,
priv
->
cursor
-
s
);
if
(
priv
->
scroll
<
s
)
{
offset
=
gnt_util_onscreen_width
(
priv
->
scroll
,
s
);
}
}
else
{
suggest
=
g_strdup
(
priv
->
start
);
}
len
=
strlen
(
suggest
);
/* Don't need to use the utf8-function here */
if
(
priv
->
ddown
==
NULL
)
{
GntWidget
*
box
=
gnt_vbox_new
(
FALSE
);
priv
->
ddown
=
gnt_tree_new
();
gnt_tree_set_compare_func
(
GNT_TREE
(
priv
->
ddown
),
(
GCompareFunc
)
g_utf8_collate
);
gnt_box_add_widget
(
GNT_BOX
(
box
),
priv
->
ddown
);
gnt_widget_set_transient
(
box
,
TRUE
);
gnt_widget_get_position
(
GNT_WIDGET
(
entry
),
&
x
,
&
y
);
x
+=
offset
;
y
++
;
if
(
y
+
10
>=
getmaxy
(
stdscr
))
y
-=
11
;
gnt_widget_set_position
(
box
,
x
,
y
);
}
else
{
gnt_tree_remove_all
(
GNT_TREE
(
priv
->
ddown
));
}
for
(
count
=
0
,
iter
=
priv
->
suggests
;
iter
;
iter
=
iter
->
next
)
{
text
=
iter
->
data
;
if
(
g_ascii_strncasecmp
(
suggest
,
text
,
len
)
==
0
&&
strlen
(
text
)
>=
len
)
{
gnt_tree_add_row_after
(
GNT_TREE
(
priv
->
ddown
),
(
gpointer
)
text
,
gnt_tree_create_row
(
GNT_TREE
(
priv
->
ddown
),
text
),
NULL
,
NULL
);
count
++
;
if
(
max
==
-1
)
max
=
strlen
(
text
)
-
len
;
else
if
(
max
)
max
=
MIN
(
max
,
max_common_prefix
(
sgst
+
len
,
text
+
len
));
sgst
=
text
;
}
}
g_free
(
suggest
);
if
(
count
==
0
)
{
destroy_suggest
(
priv
);
return
FALSE
;
}
else
if
(
count
==
1
)
{
char
*
store
=
g_strndup
(
priv
->
start
,
priv
->
end
-
priv
->
start
);
gboolean
ret
;
destroy_suggest
(
priv
);
complete_suggest
(
entry
,
sgst
);
ret
=
(
strncmp
(
store
,
priv
->
start
,
priv
->
end
-
priv
->
start
)
!=
0
);
g_free
(
store
);
return
ret
;
}
else
{
if
(
max
>
0
)
{
GntWidget
*
ddown
=
priv
->
ddown
;
char
*
match
=
g_strndup
(
sgst
+
len
,
max
);
priv
->
ddown
=
NULL
;
gnt_entry_key_pressed
(
GNT_WIDGET
(
entry
),
match
);
g_free
(
match
);
if
(
priv
->
ddown
)
{
gnt_widget_destroy
(
ddown
);
}
else
{
priv
->
ddown
=
ddown
;
}
}
gnt_widget_draw
(
gnt_widget_get_parent
(
priv
->
ddown
));
}
return
TRUE
;
}
static
void
gnt_entry_draw
(
GntWidget
*
widget
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
widget
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
WINDOW
*
window
=
gnt_widget_get_window
(
widget
);
gint
width
;
int
stop
;
gboolean
focus
;
int
curpos
;
if
((
focus
=
gnt_widget_has_focus
(
widget
)))
wbkgdset
(
window
,
'\0'
|
gnt_color_pair
(
GNT_COLOR_TEXT_NORMAL
));
else
wbkgdset
(
window
,
'\0'
|
gnt_color_pair
(
GNT_COLOR_HIGHLIGHT_D
));
if
(
priv
->
masked
)
{
mvwhline
(
window
,
0
,
0
,
gnt_ascii_only
()
?
'*'
:
ACS_BULLET
,
g_utf8_pointer_to_offset
(
priv
->
scroll
,
priv
->
end
));
}
else
mvwprintw
(
window
,
0
,
0
,
"%s"
,
C_
(
priv
->
scroll
));
stop
=
gnt_util_onscreen_width
(
priv
->
scroll
,
priv
->
end
);
gnt_widget_get_internal_size
(
GNT_WIDGET
(
entry
),
&
width
,
NULL
);
if
(
stop
<
width
)
{
mvwhline
(
window
,
0
,
stop
,
GNT_ENTRY_CHAR
,
width
-
stop
);
}
curpos
=
gnt_util_onscreen_width
(
priv
->
scroll
,
priv
->
cursor
);
if
(
focus
)
{
mvwchgat
(
window
,
0
,
curpos
,
1
,
A_REVERSE
,
GNT_COLOR_TEXT_NORMAL
,
NULL
);
}
(
void
)
wmove
(
window
,
0
,
curpos
);
}
static
void
gnt_entry_size_request
(
GntWidget
*
widget
)
{
if
(
!
gnt_widget_get_mapped
(
widget
))
{
gnt_widget_set_internal_size
(
widget
,
20
,
1
);
}
}
static
void
gnt_entry_map
(
GntWidget
*
widget
)
{
gint
width
,
height
;
gnt_widget_get_internal_size
(
widget
,
&
width
,
&
height
);
if
(
width
==
0
||
height
==
0
)
{
gnt_widget_size_request
(
widget
);
}
}
static
void
entry_redraw
(
GntWidget
*
widget
)
{
gnt_entry_draw
(
widget
);
gnt_widget_queue_update
(
widget
);
}
static
void
entry_text_changed
(
GntEntry
*
entry
)
{
g_signal_emit
(
entry
,
signals
[
SIG_TEXT_CHANGED
],
0
);
}
static
gboolean
move_back
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
cursor
<=
priv
->
start
)
{
return
FALSE
;
}
priv
->
cursor
=
g_utf8_find_prev_char
(
priv
->
start
,
priv
->
cursor
);
if
(
priv
->
cursor
<
priv
->
scroll
)
{
priv
->
scroll
=
priv
->
cursor
;
}
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
entry_redraw
(
GNT_WIDGET
(
entry
));
return
TRUE
;
}
static
void
scroll_to_fit
(
GntEntry
*
entry
)
{
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
gint
width
;
gnt_widget_get_internal_size
(
GNT_WIDGET
(
entry
),
&
width
,
NULL
);
while
(
gnt_util_onscreen_width
(
priv
->
scroll
,
priv
->
cursor
)
>=
width
)
{
priv
->
scroll
=
g_utf8_find_next_char
(
priv
->
scroll
,
NULL
);
}
}
static
gboolean
move_forward
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
cursor
>=
priv
->
end
)
{
return
FALSE
;
}
priv
->
cursor
=
g_utf8_find_next_char
(
priv
->
cursor
,
NULL
);
scroll_to_fit
(
entry
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
entry_redraw
(
GNT_WIDGET
(
entry
));
return
TRUE
;
}
static
gboolean
backspace
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
int
len
;
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
cursor
<=
priv
->
start
)
{
return
TRUE
;
}
len
=
priv
->
cursor
-
g_utf8_find_prev_char
(
priv
->
start
,
priv
->
cursor
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
priv
->
cursor
,
-
len
);
priv
->
cursor
-=
len
;
memmove
(
priv
->
cursor
,
priv
->
cursor
+
len
,
priv
->
end
-
priv
->
cursor
);
priv
->
end
-=
len
;
if
(
priv
->
scroll
>
priv
->
start
)
{
priv
->
scroll
=
g_utf8_find_prev_char
(
priv
->
start
,
priv
->
scroll
);
}
entry_redraw
(
GNT_WIDGET
(
entry
));
if
(
priv
->
ddown
)
{
show_suggest_dropdown
(
entry
);
}
entry_text_changed
(
entry
);
return
TRUE
;
}
static
gboolean
delkey
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
int
len
;
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
cursor
>=
priv
->
end
)
{
return
FALSE
;
}
len
=
g_utf8_find_next_char
(
priv
->
cursor
,
NULL
)
-
priv
->
cursor
;
update_kill_ring
(
priv
,
ENTRY_JAIL
,
priv
->
cursor
,
len
);
memmove
(
priv
->
cursor
,
priv
->
cursor
+
len
,
priv
->
end
-
priv
->
cursor
-
len
+
1
);
priv
->
end
-=
len
;
entry_redraw
(
GNT_WIDGET
(
entry
));
if
(
priv
->
ddown
)
{
show_suggest_dropdown
(
entry
);
}
entry_text_changed
(
entry
);
return
TRUE
;
}
static
gboolean
move_start
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
scroll
=
priv
->
cursor
=
priv
->
start
;
entry_redraw
(
GNT_WIDGET
(
entry
));
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
return
TRUE
;
}
static
gboolean
move_end
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
cursor
=
priv
->
end
;
/* This should be better than this */
scroll_to_fit
(
entry
);
entry_redraw
(
GNT_WIDGET
(
entry
));
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
return
TRUE
;
}
static
gboolean
history_next
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
histlength
&&
priv
->
history
->
prev
)
{
priv
->
history
=
priv
->
history
->
prev
;
gnt_entry_set_text_internal
(
entry
,
priv
->
history
->
data
);
destroy_suggest
(
priv
);
entry_text_changed
(
entry
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
return
TRUE
;
}
return
FALSE
;
}
static
gboolean
history_prev
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
histlength
&&
priv
->
history
->
next
)
{
if
(
priv
->
history
->
prev
==
NULL
)
{
/* Save the current contents */
char
*
text
=
g_strdup
(
gnt_entry_get_text
(
entry
));
g_free
(
priv
->
history
->
data
);
priv
->
history
->
data
=
text
;
}
priv
->
history
=
priv
->
history
->
next
;
gnt_entry_set_text_internal
(
entry
,
priv
->
history
->
data
);
destroy_suggest
(
priv
);
entry_text_changed
(
entry
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
return
TRUE
;
}
return
FALSE
;
}
static
gboolean
history_search
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
GList
*
iter
;
const
char
*
current
;
if
(
priv
->
history
->
prev
&&
priv
->
search
->
needle
)
{
current
=
priv
->
search
->
needle
;
}
else
{
current
=
gnt_entry_get_text
(
entry
);
}
if
(
!
priv
->
histlength
||
!
priv
->
history
->
next
||
!*
current
)
{
return
FALSE
;
}
for
(
iter
=
priv
->
history
->
next
;
iter
;
iter
=
iter
->
next
)
{
const
char
*
str
=
iter
->
data
;
/* A more utf8-friendly version of strstr would have been better, but
* for now, this will have to do. */
if
(
strstr
(
str
,
current
)
!=
NULL
)
break
;
}
if
(
!
iter
)
return
TRUE
;
if
(
priv
->
history
->
prev
==
NULL
)
{
/* We are doing it for the first time. Save the current contents */
char
*
text
=
g_strdup
(
gnt_entry_get_text
(
entry
));
g_free
(
priv
->
search
->
needle
);
priv
->
search
->
needle
=
g_strdup
(
current
);
g_free
(
priv
->
history
->
data
);
priv
->
history
->
data
=
text
;
}
priv
->
history
=
iter
;
gnt_entry_set_text_internal
(
entry
,
priv
->
history
->
data
);
destroy_suggest
(
priv
);
entry_text_changed
(
entry
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
return
TRUE
;
}
static
gboolean
clipboard_paste
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
gchar
*
i
,
*
text
,
*
a
,
*
all
;
text
=
i
=
gnt_get_clipboard_string
();
while
(
*
i
!=
'\0'
)
{
i
=
g_utf8_next_char
(
i
);
if
(
*
i
==
'\r'
||
*
i
==
'\n'
)
*
i
=
' '
;
}
a
=
g_strndup
(
priv
->
start
,
priv
->
cursor
-
priv
->
start
);
all
=
g_strconcat
(
a
,
text
,
priv
->
cursor
,
NULL
);
gnt_entry_set_text_internal
(
entry
,
all
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
g_free
(
a
);
g_free
(
text
);
g_free
(
all
);
return
TRUE
;
}
static
gboolean
suggest_show
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
ddown
)
{
gnt_bindable_perform_action_named
(
GNT_BINDABLE
(
priv
->
ddown
),
"move-down"
,
NULL
);
return
TRUE
;
}
return
show_suggest_dropdown
(
entry
);
}
static
gboolean
suggest_next
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
ddown
)
{
gnt_bindable_perform_action_named
(
GNT_BINDABLE
(
priv
->
ddown
),
"move-down"
,
NULL
);
return
TRUE
;
}
return
FALSE
;
}
static
gboolean
suggest_prev
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
ddown
)
{
gnt_bindable_perform_action_named
(
GNT_BINDABLE
(
priv
->
ddown
),
"move-up"
,
NULL
);
return
TRUE
;
}
return
FALSE
;
}
static
gboolean
suggest_next_page
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
ddown
)
{
gnt_bindable_perform_action_named
(
GNT_BINDABLE
(
priv
->
ddown
),
"page-down"
,
NULL
);
return
TRUE
;
}
return
FALSE
;
}
static
gboolean
suggest_prev_page
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
ddown
)
{
gnt_bindable_perform_action_named
(
GNT_BINDABLE
(
priv
->
ddown
),
"page-up"
,
NULL
);
return
TRUE
;
}
return
FALSE
;
}
static
gboolean
del_to_home
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
cursor
<=
priv
->
start
)
{
return
TRUE
;
}
update_kill_ring
(
priv
,
ENTRY_DEL_BOL
,
priv
->
start
,
priv
->
cursor
-
priv
->
start
);
memmove
(
priv
->
start
,
priv
->
cursor
,
priv
->
end
-
priv
->
cursor
);
priv
->
end
-=
(
priv
->
cursor
-
priv
->
start
);
priv
->
cursor
=
priv
->
scroll
=
priv
->
start
;
memset
(
priv
->
end
,
'\0'
,
priv
->
buffer
-
(
priv
->
end
-
priv
->
start
));
entry_redraw
(
GNT_WIDGET
(
bind
));
entry_text_changed
(
entry
);
return
TRUE
;
}
static
gboolean
del_to_end
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
priv
->
end
<=
priv
->
cursor
)
{
return
TRUE
;
}
update_kill_ring
(
priv
,
ENTRY_DEL_EOL
,
priv
->
cursor
,
priv
->
end
-
priv
->
cursor
);
priv
->
end
=
priv
->
cursor
;
memset
(
priv
->
end
,
'\0'
,
priv
->
buffer
-
(
priv
->
end
-
priv
->
start
));
entry_redraw
(
GNT_WIDGET
(
bind
));
entry_text_changed
(
entry
);
return
TRUE
;
}
#define SAME(a,b) ((g_unichar_isalnum(a) && g_unichar_isalnum(b)) || \
(g_unichar_isspace(a) && g_unichar_isspace(b)) || \
(g_unichar_iswide(a) && g_unichar_iswide(b)) || \
(g_unichar_ispunct(a) && g_unichar_ispunct(b)))
static
const
char
*
begin_word
(
const
char
*
text
,
const
char
*
begin
)
{
gunichar
ch
=
0
;
while
(
text
>
begin
&&
(
!*
text
||
g_unichar_isspace
(
g_utf8_get_char
(
text
))))
text
=
g_utf8_find_prev_char
(
begin
,
text
);
ch
=
g_utf8_get_char
(
text
);
while
((
text
=
g_utf8_find_prev_char
(
begin
,
text
))
>=
begin
)
{
gunichar
cur
=
g_utf8_get_char
(
text
);
if
(
!
SAME
(
ch
,
cur
))
break
;
}
return
(
text
?
g_utf8_find_next_char
(
text
,
NULL
)
:
begin
);
}
static
const
char
*
next_begin_word
(
const
char
*
text
,
const
char
*
end
)
{
gunichar
ch
=
0
;
while
(
text
&&
text
<
end
&&
g_unichar_isspace
(
g_utf8_get_char
(
text
)))
text
=
g_utf8_find_next_char
(
text
,
end
);
if
(
text
)
{
ch
=
g_utf8_get_char
(
text
);
while
((
text
=
g_utf8_find_next_char
(
text
,
end
))
!=
NULL
&&
text
<=
end
)
{
gunichar
cur
=
g_utf8_get_char
(
text
);
if
(
!
SAME
(
ch
,
cur
))
break
;
}
}
return
(
text
?
text
:
end
);
}
#undef SAME
static
gboolean
move_back_word
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
const
char
*
iter
=
g_utf8_find_prev_char
(
priv
->
start
,
priv
->
cursor
);
if
(
iter
<
priv
->
start
)
{
return
TRUE
;
}
iter
=
begin_word
(
iter
,
priv
->
start
);
priv
->
cursor
=
(
char
*
)
iter
;
if
(
priv
->
cursor
<
priv
->
scroll
)
{
priv
->
scroll
=
priv
->
cursor
;
}
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
entry_redraw
(
GNT_WIDGET
(
bind
));
return
TRUE
;
}
static
gboolean
del_prev_word
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntWidget
*
widget
=
GNT_WIDGET
(
bind
);
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
char
*
iter
=
g_utf8_find_prev_char
(
priv
->
start
,
priv
->
cursor
);
int
count
;
if
(
iter
<
priv
->
start
)
{
return
TRUE
;
}
iter
=
(
char
*
)
begin_word
(
iter
,
priv
->
start
);
count
=
priv
->
cursor
-
iter
;
update_kill_ring
(
priv
,
ENTRY_DEL_BWD_WORD
,
iter
,
count
);
memmove
(
iter
,
priv
->
cursor
,
priv
->
end
-
priv
->
cursor
);
priv
->
end
-=
count
;
priv
->
cursor
=
iter
;
if
(
priv
->
cursor
<=
priv
->
scroll
)
{
gint
width
;
gnt_widget_get_internal_size
(
widget
,
&
width
,
NULL
);
priv
->
scroll
=
priv
->
cursor
-
width
+
2
;
if
(
priv
->
scroll
<
priv
->
start
)
{
priv
->
scroll
=
priv
->
start
;
}
}
memset
(
priv
->
end
,
'\0'
,
priv
->
buffer
-
(
priv
->
end
-
priv
->
start
));
entry_redraw
(
widget
);
entry_text_changed
(
entry
);
return
TRUE
;
}
static
gboolean
move_forward_word
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
GntWidget
*
widget
=
GNT_WIDGET
(
bind
);
priv
->
cursor
=
(
char
*
)
next_begin_word
(
priv
->
cursor
,
priv
->
end
);
scroll_to_fit
(
entry
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
entry_redraw
(
widget
);
return
TRUE
;
}
static
gboolean
delete_forward_word
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
GntWidget
*
widget
=
GNT_WIDGET
(
bind
);
char
*
iter
=
(
char
*
)
next_begin_word
(
priv
->
cursor
,
priv
->
end
);
int
len
=
priv
->
end
-
iter
+
1
;
if
(
len
<=
0
)
return
TRUE
;
update_kill_ring
(
priv
,
ENTRY_DEL_FWD_WORD
,
priv
->
cursor
,
iter
-
priv
->
cursor
);
memmove
(
priv
->
cursor
,
iter
,
len
);
len
=
iter
-
priv
->
cursor
;
priv
->
end
-=
len
;
memset
(
priv
->
end
,
'\0'
,
len
);
entry_redraw
(
widget
);
entry_text_changed
(
entry
);
return
TRUE
;
}
static
gboolean
transpose_chars
(
GntBindable
*
bind
,
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
char
*
current
,
*
prev
;
char
hold
[
8
];
/* that's right */
if
(
priv
->
cursor
<=
priv
->
start
)
{
return
FALSE
;
}
if
(
!*
priv
->
cursor
)
{
priv
->
cursor
=
g_utf8_find_prev_char
(
priv
->
start
,
priv
->
cursor
);
}
current
=
priv
->
cursor
;
prev
=
g_utf8_find_prev_char
(
priv
->
start
,
priv
->
cursor
);
move_forward
(
bind
,
params
);
/* Let's do this dance! */
memcpy
(
hold
,
prev
,
current
-
prev
);
memmove
(
prev
,
current
,
priv
->
cursor
-
current
);
memcpy
(
prev
+
(
priv
->
cursor
-
current
),
hold
,
current
-
prev
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
entry_redraw
(
GNT_WIDGET
(
entry
));
entry_text_changed
(
entry
);
return
TRUE
;
}
static
gboolean
entry_yank
(
GntBindable
*
bind
,
G_GNUC_UNUSED
GList
*
params
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
bind
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
gnt_entry_key_pressed
(
GNT_WIDGET
(
entry
),
priv
->
killring
->
buffer
->
str
);
return
TRUE
;
}
static
gboolean
gnt_entry_key_pressed
(
GntWidget
*
widget
,
const
char
*
text
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
widget
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
text
[
0
]
==
27
)
{
if
(
text
[
1
]
==
0
)
{
destroy_suggest
(
priv
);
return
TRUE
;
}
return
FALSE
;
}
if
((
text
[
0
]
==
'\r'
||
text
[
0
]
==
' '
||
text
[
0
]
==
'\n'
)
&&
priv
->
ddown
)
{
char
*
text
=
g_strdup
(
gnt_tree_get_selection_data
(
GNT_TREE
(
priv
->
ddown
)));
destroy_suggest
(
priv
);
complete_suggest
(
entry
,
text
);
g_free
(
text
);
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
entry_text_changed
(
entry
);
return
TRUE
;
}
if
(
!
iscntrl
(
text
[
0
]))
{
const
char
*
str
,
*
next
;
for
(
str
=
text
;
*
str
;
str
=
next
)
{
gsize
len
;
next
=
g_utf8_find_next_char
(
str
,
NULL
);
len
=
next
-
str
;
/* Valid input? */
/* XXX: Is it necessary to use _unichar_ variants here? */
if
(
ispunct
(
*
str
)
&&
(
priv
->
flag
&
GNT_ENTRY_FLAG_NO_PUNCT
))
{
continue
;
}
if
(
isspace
(
*
str
)
&&
(
priv
->
flag
&
GNT_ENTRY_FLAG_NO_SPACE
))
{
continue
;
}
if
(
isalpha
(
*
str
)
&&
!
(
priv
->
flag
&
GNT_ENTRY_FLAG_ALPHA
))
{
continue
;
}
if
(
isdigit
(
*
str
)
&&
!
(
priv
->
flag
&
GNT_ENTRY_FLAG_INT
))
{
continue
;
}
/* Reached the max? */
if
(
priv
->
max
&&
g_utf8_pointer_to_offset
(
priv
->
start
,
priv
->
end
)
>=
priv
->
max
)
{
continue
;
}
if
((
gsize
)(
priv
->
end
+
len
-
priv
->
start
)
>=
priv
->
buffer
)
{
/* This will cause the buffer to grow */
char
*
tmp
=
g_strdup
(
priv
->
start
);
gnt_entry_set_text_internal
(
entry
,
tmp
);
g_free
(
tmp
);
}
memmove
(
priv
->
cursor
+
len
,
priv
->
cursor
,
priv
->
end
-
priv
->
cursor
+
1
);
priv
->
end
+=
len
;
while
(
str
<
next
)
{
if
(
*
str
==
'\r'
||
*
str
==
'\n'
)
*
priv
->
cursor
=
' '
;
else
*
priv
->
cursor
=
*
str
;
priv
->
cursor
++
;
str
++
;
}
scroll_to_fit
(
entry
);
if
(
priv
->
ddown
)
{
show_suggest_dropdown
(
entry
);
}
}
update_kill_ring
(
priv
,
ENTRY_JAIL
,
NULL
,
0
);
entry_redraw
(
widget
);
entry_text_changed
(
entry
);
return
TRUE
;
}
if
(
text
[
0
]
==
'\r'
||
text
[
0
]
==
'\n'
)
{
gnt_widget_activate
(
widget
);
return
TRUE
;
}
return
FALSE
;
}
static
void
jail_killring
(
GntEntryKillRing
*
kr
)
{
g_string_free
(
kr
->
buffer
,
TRUE
);
g_free
(
kr
);
}
static
void
gnt_entry_destroy
(
GntWidget
*
widget
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
widget
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
g_free
(
priv
->
start
);
if
(
priv
->
history
)
{
priv
->
history
=
g_list_first
(
priv
->
history
);
g_list_free_full
(
priv
->
history
,
g_free
);
}
if
(
priv
->
suggests
)
{
g_list_free_full
(
priv
->
suggests
,
g_free
);
}
if
(
priv
->
ddown
)
{
gnt_widget_destroy
(
gnt_widget_get_parent
(
priv
->
ddown
));
}
g_free
(
priv
->
search
->
needle
);
g_free
(
priv
->
search
);
jail_killring
(
priv
->
killring
);
}
static
void
gnt_entry_lost_focus
(
GntWidget
*
widget
)
{
GntEntry
*
entry
=
GNT_ENTRY
(
widget
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
destroy_suggest
(
priv
);
entry_redraw
(
widget
);
}
static
gboolean
gnt_entry_clicked
(
GntWidget
*
widget
,
GntMouseEvent
event
,
G_GNUC_UNUSED
int
x
,
G_GNUC_UNUSED
int
y
)
{
if
(
event
==
GNT_MIDDLE_MOUSE_DOWN
)
{
clipboard_paste
(
GNT_BINDABLE
(
widget
),
NULL
);
return
TRUE
;
}
return
FALSE
;
}
static
void
gnt_entry_class_init
(
GntEntryClass
*
klass
)
{
GntBindableClass
*
bindable
=
GNT_BINDABLE_CLASS
(
klass
);
GntWidgetClass
*
widget_class
=
GNT_WIDGET_CLASS
(
klass
);
char
s
[
3
]
=
{
'\033'
,
erasechar
(),
0
};
widget_class
->
clicked
=
gnt_entry_clicked
;
widget_class
->
destroy
=
gnt_entry_destroy
;
widget_class
->
draw
=
gnt_entry_draw
;
widget_class
->
map
=
gnt_entry_map
;
widget_class
->
size_request
=
gnt_entry_size_request
;
widget_class
->
key_pressed
=
gnt_entry_key_pressed
;
widget_class
->
lost_focus
=
gnt_entry_lost_focus
;
signals
[
SIG_TEXT_CHANGED
]
=
g_signal_new
(
"text_changed"
,
G_TYPE_FROM_CLASS
(
klass
),
G_SIGNAL_RUN_LAST
,
G_STRUCT_OFFSET
(
GntEntryClass
,
text_changed
),
NULL
,
NULL
,
NULL
,
G_TYPE_NONE
,
0
);
/**
* GntEntry::completion:
*
* Since: 2.1.0
*/
signals
[
SIG_COMPLETION
]
=
g_signal_new
(
"completion"
,
G_TYPE_FROM_CLASS
(
klass
),
G_SIGNAL_RUN_LAST
,
0
,
NULL
,
NULL
,
NULL
,
G_TYPE_NONE
,
2
,
G_TYPE_POINTER
,
G_TYPE_POINTER
);
gnt_bindable_class_register_action
(
bindable
,
"cursor-home"
,
move_start
,
GNT_KEY_CTRL_A
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"cursor-home"
,
GNT_KEY_HOME
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"cursor-end"
,
move_end
,
GNT_KEY_CTRL_E
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"cursor-end"
,
GNT_KEY_END
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"delete-prev"
,
backspace
,
GNT_KEY_BACKSPACE
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"delete-prev"
,
s
+
1
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"delete-prev"
,
GNT_KEY_CTRL_H
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"delete-next"
,
delkey
,
GNT_KEY_DEL
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"delete-next"
,
GNT_KEY_CTRL_D
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"delete-start"
,
del_to_home
,
GNT_KEY_CTRL_U
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"delete-end"
,
del_to_end
,
GNT_KEY_CTRL_K
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"delete-prev-word"
,
del_prev_word
,
GNT_KEY_CTRL_W
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"delete-prev-word"
,
s
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"cursor-prev-word"
,
move_back_word
,
"
\033
"
"b"
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"cursor-prev"
,
move_back
,
GNT_KEY_LEFT
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"cursor-prev"
,
GNT_KEY_CTRL_B
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"cursor-next"
,
move_forward
,
GNT_KEY_RIGHT
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"cursor-next"
,
GNT_KEY_CTRL_F
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"cursor-next-word"
,
move_forward_word
,
"
\033
"
"f"
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"delete-next-word"
,
delete_forward_word
,
"
\033
"
"d"
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"transpose-chars"
,
transpose_chars
,
GNT_KEY_CTRL_T
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"yank"
,
entry_yank
,
GNT_KEY_CTRL_Y
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"suggest-show"
,
suggest_show
,
"
\t
"
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"suggest-next"
,
suggest_next
,
GNT_KEY_DOWN
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"suggest-prev"
,
suggest_prev
,
GNT_KEY_UP
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"suggest-next-page"
,
suggest_next_page
,
GNT_KEY_PGDOWN
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"suggest-prev-page"
,
suggest_prev_page
,
GNT_KEY_PGUP
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"history-next"
,
history_next
,
GNT_KEY_CTRL_DOWN
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"history-prev"
,
history_prev
,
GNT_KEY_CTRL_UP
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"history-prev"
,
GNT_KEY_CTRL_P
,
NULL
);
gnt_bindable_register_binding
(
bindable
,
"history-next"
,
GNT_KEY_CTRL_N
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"history-search"
,
history_search
,
GNT_KEY_CTRL_R
,
NULL
);
gnt_bindable_class_register_action
(
bindable
,
"clipboard-paste"
,
clipboard_paste
,
GNT_KEY_CTRL_V
,
NULL
);
gnt_style_read_actions
(
G_OBJECT_CLASS_TYPE
(
klass
),
GNT_BINDABLE_CLASS
(
klass
));
}
static
GntEntryKillRing
*
new_killring
(
void
)
{
GntEntryKillRing
*
kr
=
g_new0
(
GntEntryKillRing
,
1
);
kr
->
buffer
=
g_string_new
(
NULL
);
return
kr
;
}
static
void
gnt_entry_init
(
GntEntry
*
entry
)
{
GntWidget
*
widget
=
GNT_WIDGET
(
entry
);
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
flag
=
GNT_ENTRY_FLAG_ALL
;
priv
->
max
=
0
;
priv
->
histlength
=
0
;
priv
->
history
=
NULL
;
priv
->
word
=
TRUE
;
priv
->
always
=
FALSE
;
priv
->
suggests
=
NULL
;
priv
->
killring
=
new_killring
();
priv
->
search
=
g_new0
(
GntEntrySearch
,
1
);
gnt_widget_set_has_border
(
widget
,
FALSE
);
gnt_widget_set_has_shadow
(
widget
,
FALSE
);
gnt_widget_set_take_focus
(
widget
,
TRUE
);
gnt_widget_set_grow_x
(
widget
,
TRUE
);
gnt_widget_set_minimum_size
(
widget
,
3
,
1
);
}
/******************************************************************************
* GntEntry API
*****************************************************************************/
GntWidget
*
gnt_entry_new
(
const
char
*
text
)
{
GntWidget
*
widget
=
g_object_new
(
GNT_TYPE_ENTRY
,
NULL
);
GntEntry
*
entry
=
GNT_ENTRY
(
widget
);
gnt_entry_set_text_internal
(
entry
,
text
);
return
widget
;
}
static
void
gnt_entry_set_text_internal
(
GntEntry
*
entry
,
const
char
*
text
)
{
GntEntryPrivate
*
priv
=
gnt_entry_get_instance_private
(
entry
);
int
len
;
int
scroll
,
cursor
;
g_free
(
priv
->
start
);
if
(
text
&&
text
[
0
])
{
len
=
strlen
(
text
);
}
else
{
len
=
0
;
}
priv
->
buffer
=
len
+
128
;
scroll
=
priv
->
scroll
-
priv
->
start
;
cursor
=
priv
->
end
-
priv
->
cursor
;
priv
->
start
=
g_new0
(
char
,
priv
->
buffer
);
if
(
text
)
{
snprintf
(
priv
->
start
,
len
+
1
,
"%s"
,
text
);
}
priv
->
end
=
priv
->
start
+
len
;
if
((
priv
->
scroll
=
priv
->
start
+
scroll
)
>
priv
->
end
)
{
priv
->
scroll
=
priv
->
end
;
}
if
((
priv
->
cursor
=
priv
->
end
-
cursor
)
>
priv
->
end
)
{
priv
->
cursor
=
priv
->
end
;
}
if
(
gnt_widget_get_mapped
(
GNT_WIDGET
(
entry
)))
entry_redraw
(
GNT_WIDGET
(
entry
));
}
void
gnt_entry_set_text
(
GntEntry
*
entry
,
const
char
*
text
)
{
GntEntryPrivate
*
priv
=
NULL
;
gboolean
changed
=
TRUE
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
text
==
NULL
&&
priv
->
start
==
NULL
)
{
changed
=
FALSE
;
}
if
(
text
&&
priv
->
start
&&
g_utf8_collate
(
text
,
priv
->
start
)
==
0
)
{
changed
=
FALSE
;
}
gnt_entry_set_text_internal
(
entry
,
text
);
if
(
changed
)
entry_text_changed
(
entry
);
}
void
gnt_entry_set_max
(
GntEntry
*
entry
,
int
max
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
max
=
max
;
}
void
gnt_entry_set_flag
(
GntEntry
*
entry
,
GntEntryFlag
flag
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
flag
=
flag
;
/* XXX: Check the existing string to make sure the flags are respected? */
}
const
char
*
gnt_entry_get_text
(
GntEntry
*
entry
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_val_if_fail
(
GNT_IS_ENTRY
(
entry
),
NULL
);
priv
=
gnt_entry_get_instance_private
(
entry
);
return
priv
->
start
;
}
void
gnt_entry_clear
(
GntEntry
*
entry
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
gnt_entry_set_text_internal
(
entry
,
NULL
);
priv
->
scroll
=
priv
->
cursor
=
priv
->
end
=
priv
->
start
;
entry_redraw
(
GNT_WIDGET
(
entry
));
destroy_suggest
(
priv
);
entry_text_changed
(
entry
);
}
void
gnt_entry_set_masked
(
GntEntry
*
entry
,
gboolean
set
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
masked
=
set
;
}
void
gnt_entry_add_to_history
(
GntEntry
*
entry
,
const
char
*
text
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
/* Must have called set_history_length first */
g_return_if_fail
(
priv
->
history
!=
NULL
);
if
(
priv
->
histlength
>=
0
&&
g_list_length
(
priv
->
history
)
>=
(
gsize
)
priv
->
histlength
)
{
return
;
}
priv
->
history
=
g_list_first
(
priv
->
history
);
g_free
(
priv
->
history
->
data
);
priv
->
history
->
data
=
g_strdup
(
text
);
priv
->
history
=
g_list_prepend
(
priv
->
history
,
NULL
);
}
void
gnt_entry_set_history_length
(
GntEntry
*
entry
,
int
num
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
num
==
0
)
{
priv
->
histlength
=
num
;
if
(
priv
->
history
)
{
priv
->
history
=
g_list_first
(
priv
->
history
);
g_list_free_full
(
priv
->
history
,
g_free
);
priv
->
history
=
NULL
;
}
return
;
}
if
(
priv
->
histlength
==
0
)
{
priv
->
histlength
=
num
;
priv
->
history
=
g_list_append
(
NULL
,
NULL
);
return
;
}
if
(
num
>
0
&&
num
<
priv
->
histlength
)
{
GList
*
first
,
*
iter
;
int
index
=
0
;
for
(
first
=
priv
->
history
,
index
=
0
;
first
->
prev
;
first
=
first
->
prev
,
index
++
)
{
/* Nothing. */
}
while
((
iter
=
g_list_nth
(
first
,
num
))
!=
NULL
)
{
g_free
(
iter
->
data
);
first
=
g_list_delete_link
(
first
,
iter
);
}
priv
->
histlength
=
num
;
if
(
index
>=
num
)
priv
->
history
=
g_list_last
(
first
);
return
;
}
priv
->
histlength
=
num
;
}
void
gnt_entry_set_word_suggest
(
GntEntry
*
entry
,
gboolean
word
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
word
=
word
;
}
void
gnt_entry_set_always_suggest
(
GntEntry
*
entry
,
gboolean
always
)
{
GntEntryPrivate
*
priv
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
priv
->
always
=
always
;
}
void
gnt_entry_add_suggest
(
GntEntry
*
entry
,
const
char
*
text
)
{
GntEntryPrivate
*
priv
=
NULL
;
GList
*
find
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
if
(
!
text
||
!*
text
)
return
;
find
=
g_list_find_custom
(
priv
->
suggests
,
text
,
(
GCompareFunc
)
g_utf8_collate
);
if
(
find
)
return
;
priv
->
suggests
=
g_list_append
(
priv
->
suggests
,
g_strdup
(
text
));
}
void
gnt_entry_remove_suggest
(
GntEntry
*
entry
,
const
char
*
text
)
{
GntEntryPrivate
*
priv
=
NULL
;
GList
*
find
=
NULL
;
g_return_if_fail
(
GNT_IS_ENTRY
(
entry
));
priv
=
gnt_entry_get_instance_private
(
entry
);
find
=
g_list_find_custom
(
priv
->
suggests
,
text
,
(
GCompareFunc
)
g_utf8_collate
);
if
(
find
)
{
g_free
(
find
->
data
);
priv
->
suggests
=
g_list_delete_link
(
priv
->
suggests
,
find
);
}
}