rofi  1.5.4
combi.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2017 Qball Cow <qball@gmpclient.org>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
29 #define G_LOG_DOMAIN "Dialogs.Combi"
30 
31 #include <config.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <rofi.h>
35 #include "settings.h"
36 #include "helper.h"
37 
38 #include <dialogs/dialogs.h>
39 #include <pango/pango.h>
40 #include "mode-private.h"
41 #include <theme.h>
42 
46 typedef struct
47 {
49  gboolean disable;
50 } CombiMode;
51 
52 typedef struct
53 {
54  // List of (combined) entries.
55  unsigned int cmd_list_length;
56  // List to validate where each switcher starts.
57  unsigned int *starts;
58  unsigned int *lengths;
59  // List of switchers to combine.
60  unsigned int num_switchers;
63 
64 static void combi_mode_parse_switchers ( Mode *sw )
65 {
67  char *savept = NULL;
68  // Make a copy, as strtok will modify it.
69  char *switcher_str = g_strdup ( config.combi_modi );
70  const char * const sep = ",#";
71  // Split token on ','. This modifies switcher_str.
72  for ( char *token = strtok_r ( switcher_str, sep, &savept ); token != NULL;
73  token = strtok_r ( NULL, sep, &savept ) ) {
74  // Resize and add entry.
75  pd->switchers = (CombiMode *) g_realloc ( pd->switchers,
76  sizeof ( CombiMode ) * ( pd->num_switchers + 1 ) );
77 
78  Mode *mode = rofi_collect_modi_search ( token );
79  if ( mode ) {
80  pd->switchers[pd->num_switchers].disable = FALSE;
81  pd->switchers[pd->num_switchers++].mode = mode;
82  }
83  else {
84  // If not build in, use custom switchers.
85  Mode *sw = script_switcher_parse_setup ( token );
86  if ( sw != NULL ) {
87  pd->switchers[pd->num_switchers].disable = FALSE;
88  pd->switchers[pd->num_switchers++].mode = sw;
89  }
90  else {
91  // Report error, don't continue.
92  g_warning ( "Invalid script switcher: %s", token );
93  token = NULL;
94  }
95  }
96  }
97  // Free string that was modified by strtok_r
98  g_free ( switcher_str );
99 }
100 
101 static int combi_mode_init ( Mode *sw )
102 {
103  if ( mode_get_private_data ( sw ) == NULL ) {
104  CombiModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
105  mode_set_private_data ( sw, (void *) pd );
107  pd->starts = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
108  pd->lengths = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
109  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
110  if ( !mode_init ( pd->switchers[i].mode ) ) {
111  return FALSE;
112  }
113  }
114  if ( pd->cmd_list_length == 0 ) {
115  pd->cmd_list_length = 0;
116  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
117  unsigned int length = mode_get_num_entries ( pd->switchers[i].mode );
118  pd->starts[i] = pd->cmd_list_length;
119  pd->lengths[i] = length;
120  pd->cmd_list_length += length;
121  }
122  }
123  }
124  return TRUE;
125 }
126 static unsigned int combi_mode_get_num_entries ( const Mode *sw )
127 {
129  unsigned int length = 0;
130  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
131  unsigned int entries = mode_get_num_entries ( pd->switchers[i].mode );
132  pd->starts[i] = length;
133  pd->lengths[i] = entries;
134  length += entries;
135  }
136  return length;
137 }
138 static void combi_mode_destroy ( Mode *sw )
139 {
141  if ( pd != NULL ) {
142  g_free ( pd->starts );
143  g_free ( pd->lengths );
144  // Cleanup switchers.
145  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
146  mode_destroy ( pd->switchers[i].mode );
147  }
148  g_free ( pd->switchers );
149  g_free ( pd );
150  mode_set_private_data ( sw, NULL );
151  }
152 }
153 static ModeMode combi_mode_result ( Mode *sw, int mretv, char **input, unsigned int selected_line )
154 {
156 
157  if ( input[0][0] == '!' ) {
158  int switcher = -1;
159  char *eob = strchrnul ( input[0], ' ' );
160  ssize_t bang_len = g_utf8_pointer_to_offset ( input[0], eob ) - 1;
161  if ( bang_len > 0 ) {
162  for ( unsigned i = 0; switcher == -1 && i < pd->num_switchers; i++ ) {
163  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
164  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
165  if ( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[0][1], mode_name, bang_len ) == 0 ) {
166  switcher = i;
167  }
168  }
169  }
170  if ( switcher >= 0 ) {
171  if ( eob[0] == ' ' ) {
172  char *n = eob + 1;
173  return mode_result ( pd->switchers[switcher].mode, mretv, &n,
174  selected_line - pd->starts[switcher] );
175  }
176  return MODE_EXIT;
177  }
178  }
179  if ( mretv & MENU_QUICK_SWITCH ) {
180  return mretv & MENU_LOWER_MASK;
181  }
182 
183  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
184  if ( selected_line >= pd->starts[i] &&
185  selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
186  return mode_result ( pd->switchers[i].mode, mretv, input, selected_line - pd->starts[i] );
187  }
188  }
189  return MODE_EXIT;
190 }
191 static int combi_mode_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
192 {
194  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
195  if ( pd->switchers[i].disable ) {
196  continue;
197  }
198  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
199  return mode_token_match ( pd->switchers[i].mode, tokens, index - pd->starts[i] );
200  }
201  }
202  return 0;
203 }
204 static char * combi_mgrv ( const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry )
205 {
207  if ( !get_entry ) {
208  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
209  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
210  mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, FALSE );
211  return NULL;
212  }
213  }
214  return NULL;
215  }
216  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
217  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
218  char * retv;
219  char * str = retv = mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, TRUE );
220  const char *dname = mode_get_display_name ( pd->switchers[i].mode );
222  retv = g_strdup_printf ( "%s %s", dname, str );
223  g_free ( str );
224  }
225 
226  if ( attr_list != NULL ) {
227  ThemeWidget *wid = rofi_theme_find_widget ( sw->name, NULL, TRUE );
228  Property *p = rofi_theme_find_property ( wid, P_COLOR, pd->switchers[i].mode->name, TRUE );
229  if ( p != NULL ) {
230  PangoAttribute *pa = pango_attr_foreground_new (
231  p->value.color.red * 65535,
232  p->value.color.green * 65535,
233  p->value.color.blue * 65535 );
234  pa->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
235  pa->end_index = strlen ( dname );
236  *attr_list = g_list_append ( *attr_list, pa );
237  }
238  }
239  return retv;
240  }
241  }
242 
243  return NULL;
244 }
245 static char * combi_get_completion ( const Mode *sw, unsigned int index )
246 {
248  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
249  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
250  char *comp = mode_get_completion ( pd->switchers[i].mode, index - pd->starts[i] );
251  char *mcomp = g_strdup_printf ( "!%s %s", mode_get_name ( pd->switchers[i].mode ), comp );
252  g_free ( comp );
253  return mcomp;
254  }
255  }
256  // Should never get here.
257  g_assert_not_reached ();
258  return NULL;
259 }
260 
261 static cairo_surface_t * combi_get_icon ( const Mode *sw, unsigned int index, int height )
262 {
264  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
265  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
266  cairo_surface_t *icon = mode_get_icon ( pd->switchers[i].mode, index - pd->starts[i], height );
267  return icon;
268  }
269  }
270  return NULL;
271 }
272 
273 static char * combi_preprocess_input ( Mode *sw, const char *input )
274 {
276  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
277  pd->switchers[i].disable = FALSE;
278  }
279  if ( input != NULL && input[0] == '!' ) {
280  char *eob = strchrnul ( input, ' ' );
281  ssize_t bang_len = g_utf8_pointer_to_offset ( input, eob ) - 1;
282  if ( bang_len > 0 ) {
283  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
284  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
285  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
286  if ( !( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[1], mode_name, bang_len ) == 0 ) ) {
287  // No match.
288  pd->switchers[i].disable = TRUE;
289  }
290  }
291  if ( eob[0] == '\0' || eob[1] == '\0' ) {
292  return NULL;
293  }
294  return g_strdup ( eob + 1 );
295  }
296  }
297  return g_strdup ( input );
298 }
299 
301 {
302  .name = "combi",
303  .cfg_name_key = "display-combi",
304  ._init = combi_mode_init,
305  ._get_num_entries = combi_mode_get_num_entries,
306  ._result = combi_mode_result,
307  ._destroy = combi_mode_destroy,
308  ._token_match = combi_mode_match,
309  ._get_completion = combi_get_completion,
310  ._get_display_value = combi_mgrv,
311  ._get_icon = combi_get_icon,
312  ._preprocess_input = combi_preprocess_input,
313  .private_data = NULL,
314  .free = NULL
315 };
CombiModePrivateData::lengths
unsigned int * lengths
Definition: combi.c:58
MENU_QUICK_SWITCH
@ MENU_QUICK_SWITCH
Definition: mode.h:79
ThemeColor::green
double green
Definition: rofi-types.h:119
dialogs.h
rofi_theme_find_property
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)
Definition: theme.c:499
mode_get_completion
char * mode_get_completion(const Mode *mode, unsigned int selected_line)
Definition: mode.c:84
rofi_mode::name
char * name
Definition: mode-private.h:156
settings.h
mode_token_match
int mode_token_match(const Mode *mode, rofi_int_matcher **tokens, unsigned int selected_line)
Definition: mode.c:105
CombiModePrivateData::switchers
CombiMode * switchers
Definition: combi.c:61
CombiModePrivateData::num_switchers
unsigned int num_switchers
Definition: combi.c:60
rofi_int_matcher_t
Definition: rofi-types.h:235
ThemeColor::blue
double blue
Definition: rofi-types.h:121
combi_mode_match
static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: combi.c:191
mode_get_icon
cairo_surface_t * mode_get_icon(const Mode *mode, unsigned int selected_line, int height)
Definition: mode.c:72
mode_set_private_data
void mode_set_private_data(Mode *mode, void *pd)
Definition: mode.c:134
_icon
Definition: icon.c:41
combi_mode_destroy
static void combi_mode_destroy(Mode *sw)
Definition: combi.c:138
ThemeColor::red
double red
Definition: rofi-types.h:117
mode_init
int mode_init(Mode *mode)
Definition: mode.c:42
combi_mode_parse_switchers
static void combi_mode_parse_switchers(Mode *sw)
Definition: combi.c:64
theme.h
mode-private.h
CombiMode::mode
Mode * mode
Definition: combi.c:48
mode_get_name
const char * mode_get_name(const Mode *mode)
Definition: mode.c:112
script_switcher_parse_setup
Mode * script_switcher_parse_setup(const char *str)
Definition: script.c:339
utf8_strncmp
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition: helper.c:952
MODE_EXIT
@ MODE_EXIT
Definition: mode.h:52
mode_get_num_entries
unsigned int mode_get_num_entries(const Mode *mode)
Definition: mode.c:56
combi_mode_init
static int combi_mode_init(Mode *sw)
Definition: combi.c:101
rofi_theme_find_widget
ThemeWidget * rofi_theme_find_widget(const char *name, const char *state, gboolean exact)
Definition: theme.c:537
CombiMode::disable
gboolean disable
Definition: combi.c:49
Property
Definition: rofi-types.h:213
CombiMode
Definition: combi.c:47
mode_destroy
void mode_destroy(Mode *mode)
Definition: mode.c:49
rofi_collect_modi_search
Mode * rofi_collect_modi_search(const char *name)
Definition: rofi.c:468
combi_mgrv
static char * combi_mgrv(const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry)
Definition: combi.c:204
rofi_mode
Definition: mode-private.h:152
rofi.h
CombiModePrivateData::cmd_list_length
unsigned int cmd_list_length
Definition: combi.c:55
CombiModePrivateData
Definition: combi.c:53
combi_mode
Mode combi_mode
Definition: combi.c:300
combi_get_completion
static char * combi_get_completion(const Mode *sw, unsigned int index)
Definition: combi.c:245
icon
struct _icon icon
Definition: icon.h:44
mode_get_display_name
const char * mode_get_display_name(const Mode *mode)
Definition: mode.c:143
Property::value
PropertyValue value
Definition: rofi-types.h:219
CombiModePrivateData::starts
unsigned int * starts
Definition: combi.c:57
mode_get_private_data
void * mode_get_private_data(const Mode *mode)
Definition: mode.c:128
Settings::combi_hide_mode_prefix
gboolean combi_hide_mode_prefix
Definition: settings.h:181
_PropertyValue::color
ThemeColor color
Definition: rofi-types.h:190
mode_get_display_value
char * mode_get_display_value(const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry)
Definition: mode.c:63
ModeMode
ModeMode
Definition: mode.h:50
ThemeWidget
Definition: theme.h:39
combi_get_icon
static cairo_surface_t * combi_get_icon(const Mode *sw, unsigned int index, int height)
Definition: combi.c:261
combi_preprocess_input
static char * combi_preprocess_input(Mode *sw, const char *input)
Definition: combi.c:273
combi_mode_result
static ModeMode combi_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
Definition: combi.c:153
MENU_LOWER_MASK
@ MENU_LOWER_MASK
Definition: mode.h:85
mode_result
ModeMode mode_result(Mode *mode, int menu_retv, char **input, unsigned int selected_line)
Definition: mode.c:97
helper.h
config
Settings config
Settings::combi_modi
char * combi_modi
Definition: settings.h:142
P_COLOR
@ P_COLOR
Definition: rofi-types.h:21
combi_mode_get_num_entries
static unsigned int combi_mode_get_num_entries(const Mode *sw)
Definition: combi.c:126