rofi  1.6.1
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 != NULL ) {
80  pd->switchers[pd->num_switchers].disable = FALSE;
81  pd->switchers[pd->num_switchers++].mode = mode;
82  continue;
83  }
84  // If not build in, use custom switchers.
85  mode = script_switcher_parse_setup ( token );
86  if ( mode != NULL ) {
87  pd->switchers[pd->num_switchers].disable = FALSE;
88  pd->switchers[pd->num_switchers++].mode = mode;
89  continue;
90  }
91  // Report error, don't continue.
92  g_warning ( "Invalid script switcher: %s", token );
93  token = NULL;
94  }
95  // Free string that was modified by strtok_r
96  g_free ( switcher_str );
97 }
98 static unsigned int combi_mode_get_num_entries ( const Mode *sw )
99 {
101  unsigned int length = 0;
102  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
103  unsigned int entries = mode_get_num_entries ( pd->switchers[i].mode );
104  pd->starts[i] = length;
105  pd->lengths[i] = entries;
106  length += entries;
107  }
108  return length;
109 }
110 
111 static int combi_mode_init ( Mode *sw )
112 {
113  if ( mode_get_private_data ( sw ) == NULL ) {
114  CombiModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
115  mode_set_private_data ( sw, (void *) pd );
117  pd->starts = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
118  pd->lengths = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
119  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
120  if ( !mode_init ( pd->switchers[i].mode ) ) {
121  return FALSE;
122  }
123  }
124  if ( pd->cmd_list_length == 0 ) {
125  pd->cmd_list_length = combi_mode_get_num_entries ( sw );
126  }
127  }
128  return TRUE;
129 }
130 static void combi_mode_destroy ( Mode *sw )
131 {
133  if ( pd != NULL ) {
134  g_free ( pd->starts );
135  g_free ( pd->lengths );
136  // Cleanup switchers.
137  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
138  mode_destroy ( pd->switchers[i].mode );
139  }
140  g_free ( pd->switchers );
141  g_free ( pd );
142  mode_set_private_data ( sw, NULL );
143  }
144 }
145 static ModeMode combi_mode_result ( Mode *sw, int mretv, char **input, unsigned int selected_line )
146 {
148 
149  if ( input[0][0] == '!' ) {
150  int switcher = -1;
151  // Implement strchrnul behaviour.
152  char *eob = g_utf8_strchr ( input[0], -1, ' ' );
153  if ( eob == NULL ) {
154  eob = &( input[0][strlen ( input[0] )] );
155  }
156  ssize_t bang_len = g_utf8_pointer_to_offset ( input[0], eob ) - 1;
157  if ( bang_len > 0 ) {
158  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
159  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
160  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
161  if ( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[0][1], mode_name, bang_len ) == 0 ) {
162  switcher = i;
163  break;
164  }
165  }
166  }
167  if ( switcher >= 0 ) {
168  if ( eob[0] == ' ' ) {
169  char *n = eob + 1;
170  return mode_result ( pd->switchers[switcher].mode, mretv, &n,
171  selected_line - pd->starts[switcher] );
172  }
173  return MODE_EXIT;
174  }
175  }
176 
177  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
178  if ( selected_line >= pd->starts[i] &&
179  selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
180  return mode_result ( pd->switchers[i].mode, mretv, input, selected_line - pd->starts[i] );
181  }
182  }
183  if ( ( mretv & MENU_CUSTOM_INPUT ) ) {
184  return mode_result ( pd->switchers[0].mode, mretv, input, selected_line );
185  }
186  return MODE_EXIT;
187 }
188 static int combi_mode_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
189 {
191  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
192  if ( pd->switchers[i].disable ) {
193  continue;
194  }
195  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
196  return mode_token_match ( pd->switchers[i].mode, tokens, index - pd->starts[i] );
197  }
198  }
199  return 0;
200 }
201 static char * combi_mgrv ( const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry )
202 {
204  if ( !get_entry ) {
205  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
206  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
207  mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, FALSE );
208  return NULL;
209  }
210  }
211  return NULL;
212  }
213  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
214  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
215  char * retv;
216  char * str = retv = mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, TRUE );
217  const char *dname = mode_get_display_name ( pd->switchers[i].mode );
219  retv = g_strdup_printf ( "%s %s", dname, str );
220  g_free ( str );
221  }
222 
223  if ( attr_list != NULL ) {
224  ThemeWidget *wid = rofi_theme_find_widget ( sw->name, NULL, TRUE );
225  Property *p = rofi_theme_find_property ( wid, P_COLOR, pd->switchers[i].mode->name, TRUE );
226  if ( p != NULL ) {
227  PangoAttribute *pa = pango_attr_foreground_new (
228  p->value.color.red * 65535,
229  p->value.color.green * 65535,
230  p->value.color.blue * 65535 );
231  pa->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
232  pa->end_index = strlen ( dname );
233  *attr_list = g_list_append ( *attr_list, pa );
234  }
235  }
236  return retv;
237  }
238  }
239 
240  return NULL;
241 }
242 static char * combi_get_completion ( const Mode *sw, unsigned int index )
243 {
245  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
246  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
247  char *comp = mode_get_completion ( pd->switchers[i].mode, index - pd->starts[i] );
248  char *mcomp = g_strdup_printf ( "!%s %s", mode_get_name ( pd->switchers[i].mode ), comp );
249  g_free ( comp );
250  return mcomp;
251  }
252  }
253  // Should never get here.
254  g_assert_not_reached ();
255  return NULL;
256 }
257 
258 static cairo_surface_t * combi_get_icon ( const Mode *sw, unsigned int index, int height )
259 {
261  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
262  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
263  cairo_surface_t *icon = mode_get_icon ( pd->switchers[i].mode, index - pd->starts[i], height );
264  return icon;
265  }
266  }
267  return NULL;
268 }
269 
270 static char * combi_preprocess_input ( Mode *sw, const char *input )
271 {
273  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
274  pd->switchers[i].disable = FALSE;
275  }
276  if ( input != NULL && input[0] == '!' ) {
277  // Implement strchrnul behaviour.
278  const char *eob = g_utf8_strchr ( input, -1, ' ' );
279  if ( eob == NULL ) {
280  // Set it to end.
281  eob = &( input[strlen ( input )] );
282  }
283  ssize_t bang_len = g_utf8_pointer_to_offset ( input, eob ) - 1;
284  if ( bang_len > 0 ) {
285  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
286  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
287  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
288  if ( !( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[1], mode_name, bang_len ) == 0 ) ) {
289  // No match.
290  pd->switchers[i].disable = TRUE;
291  }
292  }
293  if ( eob[0] == '\0' || eob[1] == '\0' ) {
294  return NULL;
295  }
296  return g_strdup ( eob + 1 );
297  }
298  }
299  return g_strdup ( input );
300 }
301 
303 {
304  .name = "combi",
305  .cfg_name_key = "display-combi",
306  ._init = combi_mode_init,
307  ._get_num_entries = combi_mode_get_num_entries,
308  ._result = combi_mode_result,
309  ._destroy = combi_mode_destroy,
310  ._token_match = combi_mode_match,
311  ._get_completion = combi_get_completion,
312  ._get_display_value = combi_mgrv,
313  ._get_icon = combi_get_icon,
314  ._preprocess_input = combi_preprocess_input,
315  .private_data = NULL,
316  .free = NULL
317 };
static char * combi_preprocess_input(Mode *sw, const char *input)
Definition: combi.c:270
Mode * mode
Definition: combi.c:48
CombiMode * switchers
Definition: combi.c:61
int mode_token_match(const Mode *mode, rofi_int_matcher **tokens, unsigned int selected_line)
Definition: mode.c:116
ThemeColor color
Definition: rofi-types.h:224
static cairo_surface_t * combi_get_icon(const Mode *sw, unsigned int index, int height)
Definition: combi.c:258
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition: helper.c:1005
double blue
Definition: rofi-types.h:153
ThemeWidget * rofi_theme_find_widget(const char *name, const char *state, gboolean exact)
Definition: theme.c:721
static int combi_mode_init(Mode *sw)
Definition: combi.c:111
cairo_surface_t * mode_get_icon(const Mode *mode, unsigned int selected_line, int height)
Definition: mode.c:72
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)
Definition: theme.c:683
double green
Definition: rofi-types.h:151
double red
Definition: rofi-types.h:149
char * combi_modi
Definition: settings.h:151
gboolean combi_hide_mode_prefix
Definition: settings.h:190
PropertyValue value
Definition: rofi-types.h:253
static ModeMode combi_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
Definition: combi.c:145
ModeMode
Definition: mode.h:49
Definition: mode.h:52
unsigned int * starts
Definition: combi.c:57
static void combi_mode_destroy(Mode *sw)
Definition: combi.c:130
void * mode_get_private_data(const Mode *mode)
Definition: mode.c:139
unsigned int num_switchers
Definition: combi.c:60
Definition: icon.c:40
Mode * rofi_collect_modi_search(const char *name)
Definition: rofi.c:485
void mode_destroy(Mode *mode)
Definition: mode.c:49
static unsigned int combi_mode_get_num_entries(const Mode *sw)
Definition: combi.c:98
unsigned int * lengths
Definition: combi.c:58
unsigned int cmd_list_length
Definition: combi.c:55
gboolean disable
Definition: combi.c:49
static void combi_mode_parse_switchers(Mode *sw)
Definition: combi.c:64
unsigned int mode_get_num_entries(const Mode *mode)
Definition: mode.c:56
char * mode_get_display_value(const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry)
Definition: mode.c:63
const char * mode_get_name(const Mode *mode)
Definition: mode.c:123
void mode_set_private_data(Mode *mode, void *pd)
Definition: mode.c:145
Mode * script_switcher_parse_setup(const char *str)
Definition: script.c:422
ModeMode mode_result(Mode *mode, int menu_retv, char **input, unsigned int selected_line)
Definition: mode.c:97
static char * combi_get_completion(const Mode *sw, unsigned int index)
Definition: combi.c:242
Mode combi_mode
Definition: combi.c:302
struct _icon icon
Definition: icon.h:44
const char * mode_get_display_name(const Mode *mode)
Definition: mode.c:154
Settings config
static char * combi_mgrv(const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry)
Definition: combi.c:201
static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: combi.c:188
char * name
Definition: mode-private.h:156
char * mode_get_completion(const Mode *mode, unsigned int selected_line)
Definition: mode.c:84
int mode_init(Mode *mode)
Definition: mode.c:42