rofi  1.6.0
combi.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2020 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  // Implement strchrnul behaviour.
160  char *eob = g_utf8_strchr ( input[0], -1, ' ' );
161  if ( eob == NULL ) {
162  eob = &( input[0][strlen ( input[0] )] );
163  }
164  ssize_t bang_len = g_utf8_pointer_to_offset ( input[0], eob ) - 1;
165  if ( bang_len > 0 ) {
166  for ( unsigned i = 0; switcher == -1 && i < pd->num_switchers; i++ ) {
167  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
168  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
169  if ( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[0][1], mode_name, bang_len ) == 0 ) {
170  switcher = i;
171  }
172  }
173  }
174  if ( switcher >= 0 ) {
175  if ( eob[0] == ' ' ) {
176  char *n = eob + 1;
177  return mode_result ( pd->switchers[switcher].mode, mretv, &n,
178  selected_line - pd->starts[switcher] );
179  }
180  return MODE_EXIT;
181  }
182  }
183  if ( mretv & MENU_QUICK_SWITCH ) {
184  return mretv & MENU_LOWER_MASK;
185  }
186 
187  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
188  if ( selected_line >= pd->starts[i] &&
189  selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
190  return mode_result ( pd->switchers[i].mode, mretv, input, selected_line - pd->starts[i] );
191  }
192  }
193  return MODE_EXIT;
194 }
195 static int combi_mode_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
196 {
198  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
199  if ( pd->switchers[i].disable ) {
200  continue;
201  }
202  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
203  return mode_token_match ( pd->switchers[i].mode, tokens, index - pd->starts[i] );
204  }
205  }
206  return 0;
207 }
208 static char * combi_mgrv ( const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry )
209 {
211  if ( !get_entry ) {
212  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
213  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
214  mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, FALSE );
215  return NULL;
216  }
217  }
218  return NULL;
219  }
220  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
221  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
222  char * retv;
223  char * str = retv = mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, TRUE );
224  const char *dname = mode_get_display_name ( pd->switchers[i].mode );
226  retv = g_strdup_printf ( "%s %s", dname, str );
227  g_free ( str );
228  }
229 
230  if ( attr_list != NULL ) {
231  ThemeWidget *wid = rofi_theme_find_widget ( sw->name, NULL, TRUE );
232  Property *p = rofi_theme_find_property ( wid, P_COLOR, pd->switchers[i].mode->name, TRUE );
233  if ( p != NULL ) {
234  PangoAttribute *pa = pango_attr_foreground_new (
235  p->value.color.red * 65535,
236  p->value.color.green * 65535,
237  p->value.color.blue * 65535 );
238  pa->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
239  pa->end_index = strlen ( dname );
240  *attr_list = g_list_append ( *attr_list, pa );
241  }
242  }
243  return retv;
244  }
245  }
246 
247  return NULL;
248 }
249 static char * combi_get_completion ( const Mode *sw, unsigned int index )
250 {
252  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
253  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
254  char *comp = mode_get_completion ( pd->switchers[i].mode, index - pd->starts[i] );
255  char *mcomp = g_strdup_printf ( "!%s %s", mode_get_name ( pd->switchers[i].mode ), comp );
256  g_free ( comp );
257  return mcomp;
258  }
259  }
260  // Should never get here.
261  g_assert_not_reached ();
262  return NULL;
263 }
264 
265 static cairo_surface_t * combi_get_icon ( const Mode *sw, unsigned int index, int height )
266 {
268  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
269  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
270  cairo_surface_t *icon = mode_get_icon ( pd->switchers[i].mode, index - pd->starts[i], height );
271  return icon;
272  }
273  }
274  return NULL;
275 }
276 
277 static char * combi_preprocess_input ( Mode *sw, const char *input )
278 {
280  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
281  pd->switchers[i].disable = FALSE;
282  }
283  if ( input != NULL && input[0] == '!' ) {
284  // Implement strchrnul behaviour.
285  const char *eob = g_utf8_strchr ( input, -1, ' ' );
286  if ( eob == NULL ) {
287  // Set it to end.
288  eob = &( input[strlen ( input )] );
289  }
290  ssize_t bang_len = g_utf8_pointer_to_offset ( input, eob ) - 1;
291  if ( bang_len > 0 ) {
292  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
293  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
294  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
295  if ( !( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[1], mode_name, bang_len ) == 0 ) ) {
296  // No match.
297  pd->switchers[i].disable = TRUE;
298  }
299  }
300  if ( eob[0] == '\0' || eob[1] == '\0' ) {
301  return NULL;
302  }
303  return g_strdup ( eob + 1 );
304  }
305  }
306  return g_strdup ( input );
307 }
308 
310 {
311  .name = "combi",
312  .cfg_name_key = "display-combi",
313  ._init = combi_mode_init,
314  ._get_num_entries = combi_mode_get_num_entries,
315  ._result = combi_mode_result,
316  ._destroy = combi_mode_destroy,
317  ._token_match = combi_mode_match,
318  ._get_completion = combi_get_completion,
319  ._get_display_value = combi_mgrv,
320  ._get_icon = combi_get_icon,
321  ._preprocess_input = combi_preprocess_input,
322  .private_data = NULL,
323  .free = NULL
324 };
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:149
dialogs.h
rofi_theme_find_property
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)
Definition: theme.c:665
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:267
ThemeColor::blue
double blue
Definition: rofi-types.h:151
combi_mode_match
static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: combi.c:195
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:147
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:428
utf8_strncmp
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition: helper.c:958
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:703
CombiMode::disable
gboolean disable
Definition: combi.c:49
Property
Definition: rofi-types.h:245
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:478
combi_mgrv
static char * combi_mgrv(const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry)
Definition: combi.c:208
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:309
combi_get_completion
static char * combi_get_completion(const Mode *sw, unsigned int index)
Definition: combi.c:249
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:251
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:190
_PropertyValue::color
ThemeColor color
Definition: rofi-types.h:222
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:71
combi_get_icon
static cairo_surface_t * combi_get_icon(const Mode *sw, unsigned int index, int height)
Definition: combi.c:265
combi_preprocess_input
static char * combi_preprocess_input(Mode *sw, const char *input)
Definition: combi.c:277
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:151
P_COLOR
@ P_COLOR
Definition: rofi-types.h:23
combi_mode_get_num_entries
static unsigned int combi_mode_get_num_entries(const Mode *sw)
Definition: combi.c:126